blob: b08832435572dd1d471214c35a6acc8d2a4a2a12 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'base/common.dart';
import 'base/config.dart';
import 'base/platform.dart';
import 'features.dart';
import 'flutter_manifest.dart';
/// Reads configuration flags to possibly override the default flag value.
///
/// See [isEnabled] for details on how feature flag values are resolved.
interface class FlutterFeaturesConfig {
/// Creates a feature configuration reader from the provided sources.
///
/// [globalConfig] reads values stored by the `flutter config` tool, which
/// are normally in the user's `%HOME` directory (varies by system), while
/// [projectManifest] reads values from the _current_ Flutter project's
/// `pubspec.yaml`
const FlutterFeaturesConfig({
required Config globalConfig,
required Platform platform,
required FlutterManifest? projectManifest,
}) : _globalConfig = globalConfig,
_platform = platform,
_projectManifest = projectManifest;
final Config _globalConfig;
final Platform _platform;
// Can be null if no manifest file exists in the current directory.
final FlutterManifest? _projectManifest;
/// Returns whether [feature] has been turned on/off from configuration.
///
/// If the feature was not configured, or cannot be configured, returns `null`.
///
/// The value is resolved, if possible, in the following order, where if a
/// step resolves to a boolean value, no further steps are attempted:
///
///
/// ## 1. Local Project Configuration
///
/// If [Feature.configSetting] is `null`, this step is skipped.
///
/// If the value defined by the key `$configSetting` is set in `pubspec.yaml`,
/// it is returned as a boolean value.
///
/// Assuming there is a setting where `configSetting: 'enable-foo'`:
///
/// ```yaml
/// # true
/// flutter:
/// config:
/// enable-foo: true
///
/// # false
/// flutter:
/// config:
/// enable-foo: false
/// ```
///
/// ## 2. Global Tool Configuration
///
/// If [Feature.configSetting] is `null`, this step is skipped.
///
/// If the value defined by the key `$configSetting` is set in the global
/// (platform dependent) configuration file, it is returned as a boolean
/// value.
///
/// Assuming there is a setting where `configSetting: 'enable-foo'`:
///
/// ```sh
/// # future runs will treat the value as true
/// flutter config --enable-foo
///
/// # future runs will treat the value as false
/// flutter config --no-enable-foo
/// ```
///
/// ## 3. Environment Variable
///
/// If [Feature.environmentOverride] is `null`, this step is skipped.
///
/// If the value defined by the key `$environmentOverride` is equal to the
/// string `'true'` (case insensitive), returns `true`, or `false` otherwise.
///
/// Assuming there is a flag where `environmentOverride: 'ENABLE_FOO'`:
///
/// ```sh
/// # true
/// ENABLE_FOO=true flutter some-command
///
/// # true
/// ENABLE_FOO=TRUE flutter some-command
///
/// # false
/// ENABLE_FOO=false flutter some-command
///
/// # false
/// ENABLE_FOO=any-other-value flutter some-command
/// ```
bool? isEnabled(Feature feature) {
return _isEnabledByConfigValue(feature) ?? _isEnabledByPlatformEnvironment(feature);
}
bool? _isEnabledByConfigValue(Feature feature) {
// If the feature cannot be configured by local/global config settings, return null.
final String? featureName = feature.configSetting;
if (featureName == null) {
return null;
}
return _isEnabledAtProjectLevel(featureName) ?? _isEnabledByGlobalConfig(featureName);
}
bool? _isEnabledByPlatformEnvironment(Feature feature) {
// If the feature cannot be configured by an environment variable, return null.
final String? environmentName = feature.environmentOverride;
if (environmentName == null) {
return null;
}
final Object? environmentValue = _platform.environment[environmentName]?.toLowerCase();
if (environmentValue == null) {
return null;
}
return environmentValue == 'true';
}
bool? _isEnabledAtProjectLevel(String featureName) {
final Object? configSection = _projectManifest?.flutterDescriptor['config'];
if (configSection == null) {
return null;
}
if (configSection is! Map) {
throwToolExit(
'The "config" property of "flutter" in pubspec.yaml must be a map, but '
'got $configSection (${configSection.runtimeType})',
);
}
return _requireBoolOrNull(
configSection[featureName],
featureName: featureName,
source: '"flutter: config:" in pubspec.yaml',
);
}
bool? _isEnabledByGlobalConfig(String featureName) {
return _requireBoolOrNull(
_globalConfig.getValue(featureName),
featureName: featureName,
source: '"${_globalConfig.configPath}"',
);
}
static bool? _requireBoolOrNull(
Object? value, {
required String featureName,
required String source,
}) {
if (value is bool?) {
return value;
}
throwToolExit(
'The "$featureName" property in $source must be a boolean, but got $value (${value.runtimeType})',
);
}
}