summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2025-06-03 18:05:27 +0200
committerDylan Baker <dylan@pnwbakers.com>2025-08-01 07:55:49 -0700
commitfe25a14bd9d5a69379d8b598d1f57330bd1c50cd (patch)
tree95e460b9430e12797658a48f278c3614c8a5fd07
parent164c1284dac7b51c57ba6e013c4a9865c0315258 (diff)
downloadmeson-fe25a14bd9d5a69379d8b598d1f57330bd1c50cd.tar.gz
cargo: move dataclasses out of interpreter module
Extracted from a patch by Xavier Classens. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r--mesonbuild/cargo/interpreter.py335
-rw-r--r--mesonbuild/cargo/manifest.py274
2 files changed, 277 insertions, 332 deletions
diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py
index 3a7f47ce9..458f65687 100644
--- a/mesonbuild/cargo/interpreter.py
+++ b/mesonbuild/cargo/interpreter.py
@@ -19,346 +19,19 @@ import typing as T
from . import builder, version, cfg
from .toml import load_toml, TomlImplementationMissing
-from .manifest import fixup_meson_varname, CargoLock
+from .manifest import Manifest, CargoLock, fixup_meson_varname
from ..mesonlib import MesonException, MachineChoice
from .. import coredata, mlog
from ..wrap.wrap import PackageDefinition
if T.TYPE_CHECKING:
- from typing_extensions import Protocol, Self
-
- from . import manifest, raw
+ from . import raw
from .. import mparser
+ from .manifest import Dependency, SystemDependency
from ..environment import Environment
from ..interpreterbase import SubProject
from ..compilers.rust import RustCompiler
- # Copied from typeshed. Blarg that they don't expose this
- class DataclassInstance(Protocol):
- __dataclass_fields__: T.ClassVar[dict[str, dataclasses.Field[T.Any]]]
-
-_R = T.TypeVar('_R', bound='raw._BaseBuildTarget')
-
-
-_EXTRA_KEYS_WARNING = (
- "This may (unlikely) be an error in the cargo manifest, or may be a missing "
- "implementation in Meson. If this issue can be reproduced with the latest "
- "version of Meson, please help us by opening an issue at "
- "https://github.com/mesonbuild/meson/issues. Please include the crate and "
- "version that is generating this warning if possible."
-)
-
-
-def _fixup_raw_mappings(d: T.Mapping[str, T.Any], convert_version: bool = True) -> T.MutableMapping[str, T.Any]:
- """Fixup raw cargo mappings to ones more suitable for python to consume.
-
- This does the following:
- * replaces any `-` with `_`, cargo likes the former, but python dicts make
- keys with `-` in them awkward to work with
- * Convert Dependency versions from the cargo format to something meson
- understands
-
- :param d: The mapping to fix
- :return: the fixed string
- """
- raw = {fixup_meson_varname(k): v for k, v in d.items()}
- if convert_version and 'version' in raw:
- assert isinstance(raw['version'], str), 'for mypy'
- raw['version'] = version.convert(raw['version'])
- return raw
-
-
-def _handle_unknown_keys(data: T.MutableMapping[str, T.Any], cls: T.Union[DataclassInstance, T.Type[DataclassInstance]],
- msg: str) -> T.MutableMapping[str, T.Any]:
- """Remove and warn on keys that are coming from cargo, but are unknown to
- our representations.
-
- This is intended to give users the possibility of things proceeding when a
- new key is added to Cargo.toml that we don't yet handle, but to still warn
- them that things might not work.
-
- :param data: The raw data to look at
- :param cls: The Dataclass derived type that will be created
- :param msg: the header for the error message. Usually something like "In N structure".
- :return: The original data structure, but with all unknown keys removed.
- """
- unexpected = set(data) - {x.name for x in dataclasses.fields(cls)}
- if unexpected:
- mlog.warning(msg, 'has unexpected keys', '"{}".'.format(', '.join(sorted(unexpected))),
- _EXTRA_KEYS_WARNING)
- for k in unexpected:
- del data[k]
- return data
-
-
-@dataclasses.dataclass
-class Package:
-
- """Representation of a Cargo Package entry, with defaults filled in."""
-
- name: str
- version: str
- description: T.Optional[str] = None
- resolver: T.Optional[str] = None
- authors: T.List[str] = dataclasses.field(default_factory=list)
- edition: manifest.EDITION = '2015'
- rust_version: T.Optional[str] = None
- documentation: T.Optional[str] = None
- readme: T.Optional[str] = None
- homepage: T.Optional[str] = None
- repository: T.Optional[str] = None
- license: T.Optional[str] = None
- license_file: T.Optional[str] = None
- keywords: T.List[str] = dataclasses.field(default_factory=list)
- categories: T.List[str] = dataclasses.field(default_factory=list)
- workspace: T.Optional[str] = None
- build: T.Optional[str] = None
- links: T.Optional[str] = None
- exclude: T.List[str] = dataclasses.field(default_factory=list)
- include: T.List[str] = dataclasses.field(default_factory=list)
- publish: bool = True
- metadata: T.Dict[str, T.Any] = dataclasses.field(default_factory=dict)
- default_run: T.Optional[str] = None
- autolib: bool = True
- autobins: bool = True
- autoexamples: bool = True
- autotests: bool = True
- autobenches: bool = True
- api: str = dataclasses.field(init=False)
-
- def __post_init__(self) -> None:
- self.api = version.api(self.version)
-
- @classmethod
- def from_raw(cls, raw: raw.Package) -> Self:
- pkg = _fixup_raw_mappings(raw, convert_version=False)
- pkg = _handle_unknown_keys(pkg, cls, f'Package entry {pkg["name"]}')
- return cls(**pkg)
-
-@dataclasses.dataclass
-class SystemDependency:
-
- """ Representation of a Cargo system-deps entry
- https://docs.rs/system-deps/latest/system_deps
- """
-
- name: str
- version: T.List[str]
- optional: bool = False
- feature: T.Optional[str] = None
- feature_overrides: T.Dict[str, T.Dict[str, str]] = dataclasses.field(default_factory=dict)
-
- @classmethod
- def from_raw(cls, name: str, raw: T.Any) -> SystemDependency:
- if isinstance(raw, str):
- return cls(name, SystemDependency.convert_version(raw))
- name = raw.get('name', name)
- version = SystemDependency.convert_version(raw.get('version'))
- optional = raw.get('optional', False)
- feature = raw.get('feature')
- # Everything else are overrides when certain features are enabled.
- feature_overrides = {k: v for k, v in raw.items() if k not in {'name', 'version', 'optional', 'feature'}}
- return cls(name, version, optional, feature, feature_overrides)
-
- @staticmethod
- def convert_version(version: T.Optional[str]) -> T.List[str]:
- vers = version.split(',') if version is not None else []
- result: T.List[str] = []
- for v in vers:
- v = v.strip()
- if v[0] not in '><=':
- v = f'>={v}'
- result.append(v)
- return result
-
- def enabled(self, features: T.Set[str]) -> bool:
- return self.feature is None or self.feature in features
-
-@dataclasses.dataclass
-class Dependency:
-
- """Representation of a Cargo Dependency Entry."""
-
- name: dataclasses.InitVar[str]
- version: T.List[str]
- registry: T.Optional[str] = None
- git: T.Optional[str] = None
- branch: T.Optional[str] = None
- rev: T.Optional[str] = None
- path: T.Optional[str] = None
- optional: bool = False
- package: str = ''
- default_features: bool = True
- features: T.List[str] = dataclasses.field(default_factory=list)
- api: str = dataclasses.field(init=False)
-
- def __post_init__(self, name: str) -> None:
- self.package = self.package or name
- # Extract wanted API version from version constraints.
- api = set()
- for v in self.version:
- if v.startswith(('>=', '==')):
- api.add(version.api(v[2:].strip()))
- elif v.startswith('='):
- api.add(version.api(v[1:].strip()))
- if not api:
- self.api = '0'
- elif len(api) == 1:
- self.api = api.pop()
- else:
- raise MesonException(f'Cannot determine minimum API version from {self.version}.')
-
- @classmethod
- def from_raw(cls, name: str, raw: raw.DependencyV) -> Dependency:
- """Create a dependency from a raw cargo dictionary"""
- if isinstance(raw, str):
- return cls(name, version.convert(raw))
- fixed = _handle_unknown_keys(_fixup_raw_mappings(raw), cls, f'Dependency entry {name}')
- return cls(name, **fixed)
-
-
-@dataclasses.dataclass
-class BuildTarget(T.Generic[_R]):
-
- name: str
- crate_type: T.List[manifest.CRATE_TYPE] = dataclasses.field(default_factory=lambda: ['lib'])
- path: dataclasses.InitVar[T.Optional[str]] = None
-
- # https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-test-field
- # True for lib, bin, test
- test: bool = True
-
- # https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-doctest-field
- # True for lib
- doctest: bool = False
-
- # https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-bench-field
- # True for lib, bin, benchmark
- bench: bool = True
-
- # https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-doc-field
- # True for libraries and binaries
- doc: bool = False
-
- harness: bool = True
- edition: manifest.EDITION = '2015'
- required_features: T.List[str] = dataclasses.field(default_factory=list)
- plugin: bool = False
-
- @classmethod
- def from_raw(cls, raw: raw.BuildTarget) -> Self:
- name = raw.get('name', '<anonymous>')
- build = _handle_unknown_keys(_fixup_raw_mappings(raw), cls, f'Binary entry {name}')
- return cls(**build)
-
-@dataclasses.dataclass
-class Library(BuildTarget['raw.LibTarget']):
-
- """Representation of a Cargo Library Entry."""
-
- doctest: bool = True
- doc: bool = True
- path: str = os.path.join('src', 'lib.rs')
- proc_macro: bool = False
- crate_type: T.List[manifest.CRATE_TYPE] = dataclasses.field(default_factory=lambda: ['lib'])
- doc_scrape_examples: bool = True
-
- @classmethod
- def from_raw(cls, raw: raw.LibTarget, fallback_name: str) -> Self: # type: ignore[override]
- if 'name' not in raw:
- raw['name'] = fallback_name
- fixed = _fixup_raw_mappings(raw)
-
- # We need to set the name field if it's not set manually, including if
- # other fields are set in the lib section
- fixed = _handle_unknown_keys(fixed, cls, f'Library entry {fixed["name"]}')
-
- return cls(**fixed)
-
-
-@dataclasses.dataclass
-class Binary(BuildTarget['raw.BuildTarget']):
-
- """Representation of a Cargo Bin Entry."""
-
- doc: bool = True
-
-
-@dataclasses.dataclass
-class Test(BuildTarget['raw.BuildTarget']):
-
- """Representation of a Cargo Test Entry."""
-
- bench: bool = True
-
-
-@dataclasses.dataclass
-class Benchmark(BuildTarget['raw.BuildTarget']):
-
- """Representation of a Cargo Benchmark Entry."""
-
- test: bool = True
-
-
-@dataclasses.dataclass
-class Example(BuildTarget['raw.BuildTarget']):
-
- """Representation of a Cargo Example Entry."""
-
- crate_type: T.List[manifest.CRATE_TYPE] = dataclasses.field(default_factory=lambda: ['bin'])
-
-
-@dataclasses.dataclass
-class Manifest:
-
- """Cargo Manifest definition.
-
- Most of these values map up to the Cargo Manifest, but with default values
- if not provided.
-
- Cargo subprojects can contain what Meson wants to treat as multiple,
- interdependent, subprojects.
-
- :param path: the path within the cargo subproject.
- """
-
- package: Package
- dependencies: T.Dict[str, Dependency]
- dev_dependencies: T.Dict[str, Dependency]
- build_dependencies: T.Dict[str, Dependency]
- system_dependencies: T.Dict[str, SystemDependency] = dataclasses.field(init=False)
- lib: Library
- bin: T.List[Binary]
- test: T.List[Test]
- bench: T.List[Benchmark]
- example: T.List[Example]
- features: T.Dict[str, T.List[str]]
- target: T.Dict[str, T.Dict[str, Dependency]]
- path: str = ''
-
- def __post_init__(self) -> None:
- self.features.setdefault('default', [])
- self.system_dependencies = {k: SystemDependency.from_raw(k, v) for k, v in self.package.metadata.get('system-deps', {}).items()}
-
- @classmethod
- def from_raw(cls, raw: raw.Manifest, path: str = '') -> Self:
- return cls(
- package=Package.from_raw(raw['package']),
- dependencies={k: Dependency.from_raw(k, v) for k, v in raw.get('dependencies', {}).items()},
- dev_dependencies={k: Dependency.from_raw(k, v) for k, v in raw.get('dev-dependencies', {}).items()},
- build_dependencies={k: Dependency.from_raw(k, v) for k, v in raw.get('build-dependencies', {}).items()},
- lib=Library.from_raw(raw.get('lib', {}), raw['package']['name']),
- bin=[Binary.from_raw(b) for b in raw.get('bin', {})],
- test=[Test.from_raw(b) for b in raw.get('test', {})],
- bench=[Benchmark.from_raw(b) for b in raw.get('bench', {})],
- example=[Example.from_raw(b) for b in raw.get('example', {})],
- features=raw.get('features', {}),
- target={k: {k2: Dependency.from_raw(k2, v2) for k2, v2 in v.get('dependencies', {}).items()}
- for k, v in raw.get('target', {}).items()},
- path=path,
- )
-
-
def _dependency_name(package_name: str, api: str, suffix: str = '-rs') -> str:
basename = package_name[:-len(suffix)] if package_name.endswith(suffix) else package_name
return f'{basename}-{api}{suffix}'
@@ -693,7 +366,7 @@ class Interpreter:
build.block([build.function('subdir', [build.string('meson')])]))
]
- def _create_lib(self, pkg: PackageState, build: builder.Builder, crate_type: manifest.CRATE_TYPE) -> T.List[mparser.BaseNode]:
+ def _create_lib(self, pkg: PackageState, build: builder.Builder, crate_type: raw.CRATE_TYPE) -> T.List[mparser.BaseNode]:
dependencies: T.List[mparser.BaseNode] = []
dependency_map: T.Dict[mparser.BaseNode, mparser.BaseNode] = {}
for name in pkg.required_deps:
diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py
index fbe804b8b..d58939126 100644
--- a/mesonbuild/cargo/manifest.py
+++ b/mesonbuild/cargo/manifest.py
@@ -6,20 +6,25 @@
from __future__ import annotations
import dataclasses
+import os
import typing as T
+from . import version
from .. import mlog
+from ..mesonlib import MesonException
if T.TYPE_CHECKING:
- from typing_extensions import Protocol
+ from typing_extensions import Protocol, Self
from . import raw
+ from .raw import EDITION, CRATE_TYPE
# Copied from typeshed. Blarg that they don't expose this
class DataclassInstance(Protocol):
__dataclass_fields__: T.ClassVar[dict[str, dataclasses.Field[T.Any]]]
_DI = T.TypeVar('_DI', bound='DataclassInstance')
+_R = T.TypeVar('_R', bound='raw._BaseBuildTarget')
_EXTRA_KEYS_WARNING = (
"This may (unlikely) be an error in the cargo manifest, or may be a missing "
@@ -39,6 +44,10 @@ def fixup_meson_varname(name: str) -> str:
return name.replace('-', '_')
+def _depv_to_dep(depv: raw.DependencyV) -> raw.Dependency:
+ return {'version': depv} if isinstance(depv, str) else depv
+
+
def _raw_to_dataclass(raw: T.Mapping[str, object], cls: T.Type[_DI],
msg: str, **kwargs: T.Callable[[T.Any], object]) -> _DI:
"""Fixup raw cargo mappings to ones more suitable for python to consume as dataclass.
@@ -78,6 +87,269 @@ def _raw_to_dataclass(raw: T.Mapping[str, object], cls: T.Type[_DI],
@dataclasses.dataclass
+class Package:
+
+ """Representation of a Cargo Package entry, with defaults filled in."""
+
+ name: str
+ version: str
+ description: T.Optional[str] = None
+ resolver: T.Optional[str] = None
+ authors: T.List[str] = dataclasses.field(default_factory=list)
+ edition: EDITION = '2015'
+ rust_version: T.Optional[str] = None
+ documentation: T.Optional[str] = None
+ readme: T.Optional[str] = None
+ homepage: T.Optional[str] = None
+ repository: T.Optional[str] = None
+ license: T.Optional[str] = None
+ license_file: T.Optional[str] = None
+ keywords: T.List[str] = dataclasses.field(default_factory=list)
+ categories: T.List[str] = dataclasses.field(default_factory=list)
+ workspace: T.Optional[str] = None
+ build: T.Optional[str] = None
+ links: T.Optional[str] = None
+ exclude: T.List[str] = dataclasses.field(default_factory=list)
+ include: T.List[str] = dataclasses.field(default_factory=list)
+ publish: bool = True
+ metadata: T.Dict[str, T.Any] = dataclasses.field(default_factory=dict)
+ default_run: T.Optional[str] = None
+ autolib: bool = True
+ autobins: bool = True
+ autoexamples: bool = True
+ autotests: bool = True
+ autobenches: bool = True
+
+ api: str = dataclasses.field(init=False)
+
+ def __post_init__(self) -> None:
+ self.api = version.api(self.version)
+
+ @classmethod
+ def from_raw(cls, raw_pkg: raw.Package) -> Self:
+ return _raw_to_dataclass(raw_pkg, cls, f'Package entry {raw_pkg["name"]}')
+
+@dataclasses.dataclass
+class SystemDependency:
+
+ """ Representation of a Cargo system-deps entry
+ https://docs.rs/system-deps/latest/system_deps
+ """
+
+ name: str
+ version: T.List[str]
+ optional: bool = False
+ feature: T.Optional[str] = None
+ # TODO: convert values to dataclass
+ feature_overrides: T.Dict[str, T.Dict[str, str]] = dataclasses.field(default_factory=dict)
+
+ @classmethod
+ def from_raw(cls, name: str, raw: T.Union[T.Dict[str, T.Any], str]) -> SystemDependency:
+ if isinstance(raw, str):
+ return cls(name, SystemDependency.convert_version(raw))
+ name = raw.get('name', name)
+ version = SystemDependency.convert_version(raw.get('version', ''))
+ optional = raw.get('optional', False)
+ feature = raw.get('feature')
+ # Everything else are overrides when certain features are enabled.
+ feature_overrides = {k: v for k, v in raw.items() if k not in {'name', 'version', 'optional', 'feature'}}
+ return cls(name, version, optional, feature, feature_overrides)
+
+ @staticmethod
+ def convert_version(version: T.Optional[str]) -> T.List[str]:
+ vers = version.split(',') if version else []
+ result: T.List[str] = []
+ for v in vers:
+ v = v.strip()
+ if v[0] not in '><=':
+ v = f'>={v}'
+ result.append(v)
+ return result
+
+ def enabled(self, features: T.Set[str]) -> bool:
+ return self.feature is None or self.feature in features
+
+@dataclasses.dataclass
+class Dependency:
+
+ """Representation of a Cargo Dependency Entry."""
+
+ package: str
+ version: T.List[str]
+ registry: T.Optional[str] = None
+ git: T.Optional[str] = None
+ branch: T.Optional[str] = None
+ rev: T.Optional[str] = None
+ path: T.Optional[str] = None
+ optional: bool = False
+ default_features: bool = True
+ features: T.List[str] = dataclasses.field(default_factory=list)
+
+ api: str = dataclasses.field(init=False)
+
+ def __post_init__(self) -> None:
+ # Extract wanted API version from version constraints.
+ api = set()
+ for v in self.version:
+ if v.startswith(('>=', '==')):
+ api.add(version.api(v[2:].strip()))
+ elif v.startswith('='):
+ api.add(version.api(v[1:].strip()))
+ if not api:
+ self.api = '0'
+ elif len(api) == 1:
+ self.api = api.pop()
+ else:
+ raise MesonException(f'Cannot determine minimum API version from {self.version}.')
+
+ @classmethod
+ def from_raw_dict(cls, name: str, raw_dep: raw.Dependency) -> Dependency:
+ raw_dep.setdefault('package', name)
+ return _raw_to_dataclass(raw_dep, cls, f'Dependency entry {name}',
+ version=version.convert)
+
+ @classmethod
+ def from_raw(cls, name: str, raw_depv: raw.DependencyV) -> Dependency:
+ """Create a dependency from a raw cargo dictionary or string"""
+ raw_dep = _depv_to_dep(raw_depv)
+ return cls.from_raw_dict(name, raw_dep)
+
+
+@dataclasses.dataclass
+class BuildTarget(T.Generic[_R]):
+
+ name: str
+ path: str
+ crate_type: T.List[CRATE_TYPE] = dataclasses.field(default_factory=lambda: ['lib'])
+
+ # https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-test-field
+ # True for lib, bin, test
+ test: bool = True
+
+ # https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-doctest-field
+ # True for lib
+ doctest: bool = False
+
+ # https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-bench-field
+ # True for lib, bin, benchmark
+ bench: bool = True
+
+ # https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-doc-field
+ # True for libraries and binaries
+ doc: bool = False
+
+ harness: bool = True
+ edition: EDITION = '2015'
+ required_features: T.List[str] = dataclasses.field(default_factory=list)
+ plugin: bool = False
+
+ @classmethod
+ def from_raw(cls, raw: _R) -> Self:
+ name = raw.get('name', '<anonymous>')
+ return _raw_to_dataclass(raw, cls, f'Binary entry {name}')
+
+@dataclasses.dataclass
+class Library(BuildTarget['raw.LibTarget']):
+
+ """Representation of a Cargo Library Entry."""
+
+ doctest: bool = True
+ doc: bool = True
+ path: str = os.path.join('src', 'lib.rs')
+ proc_macro: bool = False
+ crate_type: T.List[CRATE_TYPE] = dataclasses.field(default_factory=lambda: ['lib'])
+ doc_scrape_examples: bool = True
+
+ @classmethod
+ def from_raw(cls, raw: raw.LibTarget, fallback_name: str) -> Self: # type: ignore[override]
+ # We need to set the name field if it's not set manually, including if
+ # other fields are set in the lib section
+ raw.setdefault('name', fallback_name)
+ return _raw_to_dataclass(raw, cls, f'Library entry {raw["name"]}')
+
+
+@dataclasses.dataclass
+class Binary(BuildTarget['raw.BuildTarget']):
+
+ """Representation of a Cargo Bin Entry."""
+
+ doc: bool = True
+
+
+@dataclasses.dataclass
+class Test(BuildTarget['raw.BuildTarget']):
+
+ """Representation of a Cargo Test Entry."""
+
+ bench: bool = True
+
+@dataclasses.dataclass
+class Benchmark(BuildTarget['raw.BuildTarget']):
+
+ """Representation of a Cargo Benchmark Entry."""
+
+ test: bool = True
+
+
+@dataclasses.dataclass
+class Example(BuildTarget['raw.BuildTarget']):
+
+ """Representation of a Cargo Example Entry."""
+
+
+@dataclasses.dataclass
+class Manifest:
+
+ """Cargo Manifest definition.
+
+ Most of these values map up to the Cargo Manifest, but with default values
+ if not provided.
+
+ Cargo subprojects can contain what Meson wants to treat as multiple,
+ interdependent, subprojects.
+
+ :param path: the path within the cargo subproject.
+ """
+
+ package: Package
+ dependencies: T.Dict[str, Dependency]
+ dev_dependencies: T.Dict[str, Dependency]
+ build_dependencies: T.Dict[str, Dependency]
+ system_dependencies: T.Dict[str, SystemDependency] = dataclasses.field(init=False)
+ lib: Library
+ bin: T.List[Binary]
+ test: T.List[Test]
+ bench: T.List[Benchmark]
+ example: T.List[Example]
+ features: T.Dict[str, T.List[str]]
+ target: T.Dict[str, T.Dict[str, Dependency]]
+
+ path: str = ''
+
+ def __post_init__(self) -> None:
+ self.features.setdefault('default', [])
+ self.system_dependencies = {k: SystemDependency.from_raw(k, v) for k, v in self.package.metadata.get('system-deps', {}).items()}
+
+ @classmethod
+ def from_raw(cls, raw: raw.Manifest, path: str = '') -> Self:
+ return cls(
+ package=Package.from_raw(raw['package']),
+ dependencies={k: Dependency.from_raw(k, v) for k, v in raw.get('dependencies', {}).items()},
+ dev_dependencies={k: Dependency.from_raw(k, v) for k, v in raw.get('dev-dependencies', {}).items()},
+ build_dependencies={k: Dependency.from_raw(k, v) for k, v in raw.get('build-dependencies', {}).items()},
+ lib=Library.from_raw(raw.get('lib', {}), raw['package']['name']),
+ bin=[Binary.from_raw(b) for b in raw.get('bin', {})],
+ test=[Test.from_raw(b) for b in raw.get('test', {})],
+ bench=[Benchmark.from_raw(b) for b in raw.get('bench', {})],
+ example=[Example.from_raw(b) for b in raw.get('example', {})],
+ features=raw.get('features', {}),
+ target={k: {k2: Dependency.from_raw(k2, v2) for k2, v2 in v.get('dependencies', {}).items()}
+ for k, v in raw.get('target', {}).items()},
+ path=path,
+ )
+
+
+@dataclasses.dataclass
class CargoLockPackage:
"""A description of a package in the Cargo.lock file format."""