| // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'package:pub_semver/pub_semver.dart'; | |
| import 'package.dart'; | |
| import 'source.dart'; | |
| import 'source/hosted.dart'; | |
| import 'source/root.dart'; | |
| import 'system_cache.dart'; | |
| import 'utils.dart'; | |
| /// A reference to a [Package], but not any particular version(s) of it. | |
| /// | |
| /// It knows the [name] of a package and a [Description] that is connected | |
| /// with a certain [Source]. This is what you need for listing available | |
| /// versions of a package. See [SystemCache.getVersions]. | |
| class PackageRef { | |
| final String name; | |
| final Description description; | |
| bool get isRoot => description is RootDescription; | |
| Source get source => description.source; | |
| /// Creates a reference to a package with the given [name], and | |
| /// [description]. | |
| PackageRef(this.name, this.description); | |
| /// Creates a reference to the given root package. | |
| static PackageRef root(Package package) => | |
| PackageRef(package.name, RootDescription(package.dir)); | |
| @override | |
| String toString([PackageDetail? detail]) { | |
| detail ??= PackageDetail.defaults; | |
| if (isRoot) return name; | |
| final buffer = StringBuffer(name); | |
| if (detail.showSource ?? description is! HostedDescription) { | |
| buffer.write(' from ${description.source}'); | |
| if (detail.showDescription) { | |
| buffer.write(' ${description.format()}'); | |
| } | |
| } | |
| return buffer.toString(); | |
| } | |
| PackageRange withConstraint(VersionConstraint constraint) => | |
| PackageRange(this, constraint); | |
| @override | |
| bool operator ==(Object other) => | |
| other is PackageRef && | |
| name == other.name && | |
| description == other.description; | |
| @override | |
| int get hashCode => Object.hash(name, description); | |
| } | |
| /// A reference to a specific version of a package. | |
| /// | |
| /// A package ID contains enough information to correctly retrieve the package. | |
| /// | |
| /// It's possible for multiple distinct package IDs to point to different | |
| /// packages that have identical contents. For example, the same package may be | |
| /// available from multiple sources. As far as Pub is concerned, those packages | |
| /// are different. | |
| /// | |
| /// Note that a package ID's [description] field is a [ResolvedDescription] | |
| /// while [PackageRef.description] and [PackageRange.description] are | |
| /// [Description]s. | |
| class PackageId { | |
| final String name; | |
| final Version version; | |
| final ResolvedDescription description; | |
| bool get isRoot => description is ResolvedRootDescription; | |
| Source get source => description.description.source; | |
| /// Creates an ID for a package with the given [name], [source], [version], | |
| /// and [description]. | |
| /// | |
| /// Since an ID's description is an implementation detail of its source, this | |
| /// should generally not be called outside of [Source] subclasses. | |
| PackageId(this.name, this.version, this.description); | |
| /// Creates an ID for the given root package. | |
| static PackageId root(Package package) => PackageId( | |
| package.name, | |
| package.version, | |
| ResolvedRootDescription(RootDescription(package.dir)), | |
| ); | |
| @override | |
| int get hashCode => Object.hash(name, version, description); | |
| @override | |
| bool operator ==(Object other) => | |
| other is PackageId && | |
| name == other.name && | |
| version == other.version && | |
| description == other.description; | |
| /// Returns a [PackageRange] that allows only [version] of this package. | |
| PackageRange toRange() => PackageRange(toRef(), version); | |
| PackageRef toRef() => PackageRef(name, description.description); | |
| @override | |
| String toString([PackageDetail? detail]) { | |
| detail ??= PackageDetail.defaults; | |
| final buffer = StringBuffer(name); | |
| if (detail.showVersion ?? !isRoot) buffer.write(' $version'); | |
| if (!isRoot && | |
| (detail.showSource ?? description is! ResolvedHostedDescription)) { | |
| buffer.write(' from ${description.description.source}'); | |
| if (detail.showDescription) { | |
| buffer.write(' ${description.format()}'); | |
| } | |
| } | |
| return buffer.toString(); | |
| } | |
| } | |
| /// A reference to a constrained range of versions of one package. | |
| /// | |
| /// This is represented as a [PackageRef] and a [VersionConstraint]. | |
| class PackageRange { | |
| final PackageRef _ref; | |
| /// The allowed package versions. | |
| final VersionConstraint constraint; | |
| String get name => _ref.name; | |
| Description get description => _ref.description; | |
| bool get isRoot => _ref.isRoot; | |
| Source get source => _ref.source; | |
| /// Creates a reference to package with the given [name], [source], | |
| /// [constraint], and [description]. | |
| /// | |
| /// Since an ID's description is an implementation detail of its source, this | |
| /// should generally not be called outside of [Source] subclasses. | |
| PackageRange(this._ref, this.constraint); | |
| /// Creates a range that selects the root package. | |
| static PackageRange root(Package package) => | |
| PackageRange(PackageRef.root(package), package.version); | |
| PackageRef toRef() => _ref; | |
| @override | |
| String toString([PackageDetail? detail]) { | |
| detail ??= PackageDetail.defaults; | |
| final buffer = StringBuffer(name); | |
| if (detail.showVersion ?? _showVersionConstraint) { | |
| buffer.write(' ${constraint.asCompatibleWithIfPossible()}'); | |
| } | |
| if (!isRoot && (detail.showSource ?? description is! HostedDescription)) { | |
| buffer.write(' from ${description.source.name}'); | |
| if (detail.showDescription) { | |
| buffer.write(' ${description.format()}'); | |
| } | |
| } | |
| return buffer.toString(); | |
| } | |
| /// Whether to include the version constraint in [toString] by default. | |
| bool get _showVersionConstraint { | |
| if (isRoot) return false; | |
| if (!constraint.isAny) return true; | |
| return description.hasMultipleVersions; | |
| } | |
| /// Returns a copy of `this` with the same semantics, but with a `^`-style | |
| /// constraint if possible. | |
| PackageRange withTerseConstraint() { | |
| final terse = constraint.asCompatibleWithIfPossible(); | |
| if (identical(terse, constraint)) return this; | |
| return PackageRange(_ref, terse); | |
| } | |
| /// Whether [id] satisfies this dependency. | |
| /// | |
| /// Specifically, whether [id] refers to the same package as `this` *and* | |
| /// [constraint] allows `id.version`. | |
| bool allows(PackageId id) => | |
| name == id.name && | |
| description == id.description.description && | |
| constraint.allows(id.version); | |
| @override | |
| int get hashCode => Object.hash(_ref, constraint); | |
| @override | |
| bool operator ==(Object other) => | |
| other is PackageRange && | |
| _ref == other._ref && | |
| other.constraint == constraint; | |
| } | |
| /// An enum of different levels of detail that can be used when displaying a | |
| /// terse package name. | |
| class PackageDetail { | |
| /// The default [PackageDetail] configuration. | |
| static const defaults = PackageDetail(); | |
| /// Whether to show the package version or version range. | |
| /// | |
| /// If this is `null`, the version is shown for all packages other than root | |
| /// [PackageId]s or [PackageRange]s with `git` or `path` sources and `any` | |
| /// constraints. | |
| final bool? showVersion; | |
| /// Whether to show the package source. | |
| /// | |
| /// If this is `null`, the source is shown for all non-hosted, non-root | |
| /// packages. It's always `true` if [showDescription] is `true`. | |
| final bool? showSource; | |
| /// Whether to show the package description. | |
| /// | |
| /// This defaults to `false`. | |
| final bool showDescription; | |
| const PackageDetail({ | |
| this.showVersion, | |
| bool? showSource, | |
| bool? showDescription, | |
| }) : showSource = showDescription == true ? true : showSource, | |
| showDescription = showDescription ?? false; | |
| /// Returns a [PackageDetail] with the maximum amount of detail between `this` | |
| /// and [other]. | |
| PackageDetail max(PackageDetail other) => PackageDetail( | |
| showVersion: showVersion! || other.showVersion!, | |
| showSource: showSource! || other.showSource!, | |
| showDescription: showDescription || other.showDescription, | |
| ); | |
| } |