blob: 5841fd38fdfc60a78cd8c269ee694158d8636f24 [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 'package:meta/meta.dart';
import '../convert.dart';
import 'error_handling_io.dart';
import 'file_system.dart';
import 'logger.dart';
import 'platform.dart';
import 'utils.dart';
/// A class to abstract configuration files.
class Config {
/// Constructs a new [Config] object from a file called [name] in the
/// current user's configuration directory as determined by the [Platform]
/// and [FileSystem].
///
/// The configuration directory defaults to $XDG_CONFIG_HOME on Linux and
/// macOS, but falls back to the home directory if a file named
/// `.flutter_$name` already exists there. On other platforms the
/// configuration file will always be a file named `.flutter_$name` in the
/// home directory.
factory Config(
String name, {
@required FileSystem fileSystem,
@required Logger logger,
@required Platform platform,
}) {
final String filePath = _configPath(platform, fileSystem, name);
final File file = fileSystem.file(filePath);
file.parent.createSync(recursive: true);
return Config.createForTesting(file, logger);
}
/// Constructs a new [Config] object from a file called [name] in
/// the given [Directory].
factory Config.test(
String name, {
@required Directory directory,
@required Logger logger,
}) => Config.createForTesting(directory.childFile('.${kConfigDir}_$name'), logger);
/// Test only access to the Config constructor.
@visibleForTesting
Config.createForTesting(File file, Logger logger) : _file = file, _logger = logger {
if (!_file.existsSync()) {
return;
}
try {
ErrorHandlingFileSystem.noExitOnFailure(() {
_values = castStringKeyedMap(json.decode(_file.readAsStringSync()));
});
} on FormatException {
_logger
..printError('Failed to decode preferences in ${_file.path}.')
..printError(
'You may need to reapply any previously saved configuration '
'with the "flutter config" command.',
);
_file.deleteSync();
} on Exception catch (err) {
_logger
..printError('Could not read preferences in ${file.path}.\n$err')
..printError(
'You may need to resolve the error above and reapply any previously '
'saved configuration with the "flutter config" command.',
);
}
}
/// The default directory name for Flutter's configs.
/// Configs will be written to the user's config path. If there is already a
/// file with the name `.${kConfigDir}_$name` in the user's home path, that
/// file will be used instead.
static const String kConfigDir = 'flutter';
/// Environment variable specified in the XDG Base Directory
/// [specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)
/// to specify the user's configuration directory.
static const String kXdgConfigHome = 'XDG_CONFIG_HOME';
/// Fallback directory in the user's home directory if `XDG_CONFIG_HOME` is
/// not defined.
static const String kXdgConfigFallback = '.config';
/// The default name for the Flutter config file.
static const String kFlutterSettings = 'settings';
final Logger _logger;
File _file;
String get configPath => _file.path;
Map<String, dynamic> _values = <String, dynamic>{};
Iterable<String> get keys => _values.keys;
bool containsKey(String key) => _values.containsKey(key);
dynamic getValue(String key) => _values[key];
void setValue(String key, Object value) {
_values[key] = value;
_flushValues();
}
void removeValue(String key) {
_values.remove(key);
_flushValues();
}
void _flushValues() {
String json = const JsonEncoder.withIndent(' ').convert(_values);
json = '$json\n';
_file.writeAsStringSync(json);
}
// Reads the process environment to find the current user's home directory.
//
// If the searched environment variables are not set, '.' is returned instead.
//
// Note that this is different from FileSystemUtils.homeDirPath.
static String _userHomePath(Platform platform) {
final String envKey = platform.isWindows ? 'APPDATA' : 'HOME';
return platform.environment[envKey] ?? '.';
}
static String _configPath(
Platform platform, FileSystem fileSystem, String name) {
final String homeDirFile =
fileSystem.path.join(_userHomePath(platform), '.${kConfigDir}_$name');
if (platform.isLinux || platform.isMacOS) {
if (fileSystem.isFileSync(homeDirFile)) {
return homeDirFile;
}
final String configDir = platform.environment[kXdgConfigHome] ??
fileSystem.path.join(_userHomePath(platform), '.config', kConfigDir);
return fileSystem.path.join(configDir, name);
}
return homeDirFile;
}
}