mirror of
https://github.com/SkinnyMind/libgit2dart.git
synced 2025-05-05 04:39:07 -04:00
feat(config): add api for config entry
This commit is contained in:
parent
cf677e488a
commit
7b8dfcc1af
6 changed files with 163 additions and 45 deletions
|
@ -13,8 +13,8 @@ void main() async {
|
||||||
final config = Config.open();
|
final config = Config.open();
|
||||||
|
|
||||||
print('All entries of system/global config:');
|
print('All entries of system/global config:');
|
||||||
for (final entry in config.variables.entries) {
|
for (final entry in config) {
|
||||||
print('${entry.key}: ${entry.value}');
|
print('${entry.name}: ${entry.value}');
|
||||||
}
|
}
|
||||||
// free() should be called on object to free memory when done.
|
// free() should be called on object to free memory when done.
|
||||||
config.free();
|
config.free();
|
||||||
|
@ -25,8 +25,8 @@ void main() async {
|
||||||
final repoConfig = Config.open('$tmpDir/.git/config');
|
final repoConfig = Config.open('$tmpDir/.git/config');
|
||||||
|
|
||||||
print('\nAll entries of repo config:');
|
print('\nAll entries of repo config:');
|
||||||
for (final entry in repoConfig.variables.entries) {
|
for (final entry in repoConfig) {
|
||||||
print('${entry.key}: ${entry.value}');
|
print('${entry.name}: ${entry.value}');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set value of config variable
|
// Set value of config variable
|
||||||
|
|
|
@ -131,10 +131,10 @@ Pointer<git_config> snapshot(Pointer<git_config> config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the value of a config variable.
|
/// Get the config entry of a config variable.
|
||||||
///
|
///
|
||||||
/// Throws a [LibGit2Error] if error occured.
|
/// Throws a [LibGit2Error] if error occured.
|
||||||
String getValue(Pointer<git_config> cfg, String variable) {
|
Pointer<git_config_entry> getEntry(Pointer<git_config> cfg, String variable) {
|
||||||
final out = calloc<Pointer<git_config_entry>>();
|
final out = calloc<Pointer<git_config_entry>>();
|
||||||
final name = variable.toNativeUtf8().cast<Int8>();
|
final name = variable.toNativeUtf8().cast<Int8>();
|
||||||
final error = libgit2.git_config_get_entry(out, cfg, name);
|
final error = libgit2.git_config_get_entry(out, cfg, name);
|
||||||
|
@ -144,7 +144,7 @@ String getValue(Pointer<git_config> cfg, String variable) {
|
||||||
if (error < 0) {
|
if (error < 0) {
|
||||||
throw LibGit2Error(libgit2.git_error_last());
|
throw LibGit2Error(libgit2.git_error_last());
|
||||||
} else {
|
} else {
|
||||||
return out.value.ref.value.cast<Utf8>().toDartString();
|
return out.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,23 +197,10 @@ void setString(Pointer<git_config> cfg, String variable, String value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over all the config variables.
|
/// Iterate over all the config variables.
|
||||||
Map<String, String> getEntries(Pointer<git_config> cfg) {
|
Pointer<git_config_iterator> iterator(Pointer<git_config> cfg) {
|
||||||
final iterator = calloc<Pointer<git_config_iterator>>();
|
final out = calloc<Pointer<git_config_iterator>>();
|
||||||
final entry = calloc<Pointer<git_config_entry>>();
|
libgit2.git_config_iterator_new(out, cfg);
|
||||||
libgit2.git_config_iterator_new(iterator, cfg);
|
return out.value;
|
||||||
var error = 0;
|
|
||||||
final entries = <String, String>{};
|
|
||||||
|
|
||||||
while (error != -31) {
|
|
||||||
error = libgit2.git_config_next(entry, iterator.value);
|
|
||||||
entries[entry.value.ref.name.cast<Utf8>().toDartString()] =
|
|
||||||
entry.value.ref.value.cast<Utf8>().toDartString();
|
|
||||||
}
|
|
||||||
|
|
||||||
calloc.free(iterator);
|
|
||||||
calloc.free(entry);
|
|
||||||
|
|
||||||
return entries;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a config variable from the config file with the highest level
|
/// Delete a config variable from the config file with the highest level
|
||||||
|
@ -298,5 +285,13 @@ void deleteMultivar(Pointer<git_config> cfg, String variable, String regexp) {
|
||||||
calloc.free(regexpC);
|
calloc.free(regexpC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Free a config iterator.
|
||||||
|
void iteratorFree(Pointer<git_config_iterator> iter) =>
|
||||||
|
libgit2.git_config_iterator_free(iter);
|
||||||
|
|
||||||
|
/// Free a config entry.
|
||||||
|
void entryFree(Pointer<git_config_entry> entry) =>
|
||||||
|
libgit2.git_config_entry_free(entry);
|
||||||
|
|
||||||
/// Free the configuration and its associated memory and files.
|
/// Free the configuration and its associated memory and files.
|
||||||
void free(Pointer<git_config> cfg) => libgit2.git_config_free(cfg);
|
void free(Pointer<git_config> cfg) => libgit2.git_config_free(cfg);
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
|
import 'dart:collection';
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:ffi/ffi.dart';
|
||||||
import 'bindings/libgit2_bindings.dart';
|
import 'bindings/libgit2_bindings.dart';
|
||||||
import 'bindings/config.dart' as bindings;
|
import 'bindings/config.dart' as bindings;
|
||||||
|
import 'git_types.dart';
|
||||||
import 'util.dart';
|
import 'util.dart';
|
||||||
|
|
||||||
class Config {
|
class Config with IterableMixin<ConfigEntry> {
|
||||||
/// Initializes a new instance of [Config] class from provided
|
/// Initializes a new instance of [Config] class from provided
|
||||||
/// pointer to config object in memory.
|
/// pointer to config object in memory.
|
||||||
///
|
///
|
||||||
|
@ -89,18 +92,15 @@ class Config {
|
||||||
/// Throws a [LibGit2Error] if error occured.
|
/// Throws a [LibGit2Error] if error occured.
|
||||||
Config get snapshot => Config(bindings.snapshot(_configPointer));
|
Config get snapshot => Config(bindings.snapshot(_configPointer));
|
||||||
|
|
||||||
/// Returns map of all the config variables and their values.
|
/// Returns the [ConfigEntry] of a [variable].
|
||||||
Map<String, String> get variables => bindings.getEntries(_configPointer);
|
ConfigEntry operator [](String variable) =>
|
||||||
|
ConfigEntry(bindings.getEntry(_configPointer, variable));
|
||||||
/// Returns the value of config [variable].
|
|
||||||
String operator [](String variable) =>
|
|
||||||
bindings.getValue(_configPointer, variable);
|
|
||||||
|
|
||||||
/// Sets the [value] of config [variable].
|
/// Sets the [value] of config [variable].
|
||||||
void operator []=(String variable, dynamic value) {
|
void operator []=(String variable, dynamic value) {
|
||||||
if (value.runtimeType == bool) {
|
if (value is bool) {
|
||||||
bindings.setBool(_configPointer, variable, value);
|
bindings.setBool(_configPointer, variable, value);
|
||||||
} else if (value.runtimeType == int) {
|
} else if (value is int) {
|
||||||
bindings.setInt(_configPointer, variable, value);
|
bindings.setInt(_configPointer, variable, value);
|
||||||
} else {
|
} else {
|
||||||
bindings.setString(_configPointer, variable, value);
|
bindings.setString(_configPointer, variable, value);
|
||||||
|
@ -141,4 +141,73 @@ class Config {
|
||||||
|
|
||||||
/// Releases memory allocated for config object.
|
/// Releases memory allocated for config object.
|
||||||
void free() => bindings.free(_configPointer);
|
void free() => bindings.free(_configPointer);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterator<ConfigEntry> get iterator =>
|
||||||
|
ConfigIterator(bindings.iterator(_configPointer));
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfigEntry {
|
||||||
|
ConfigEntry(this._configEntryPointer);
|
||||||
|
|
||||||
|
/// Pointer to memory address for allocated config entry object.
|
||||||
|
final Pointer<git_config_entry> _configEntryPointer;
|
||||||
|
|
||||||
|
/// Returns name of the entry (normalised).
|
||||||
|
String get name => _configEntryPointer.ref.name.cast<Utf8>().toDartString();
|
||||||
|
|
||||||
|
/// Returns value of the entry.
|
||||||
|
String get value => _configEntryPointer.ref.value.cast<Utf8>().toDartString();
|
||||||
|
|
||||||
|
/// Returns depth of includes where this variable was found
|
||||||
|
int get includeDepth => _configEntryPointer.ref.include_depth;
|
||||||
|
|
||||||
|
/// Returns which config file this was found in.
|
||||||
|
GitConfigLevel get level {
|
||||||
|
late GitConfigLevel result;
|
||||||
|
for (var level in GitConfigLevel.values) {
|
||||||
|
if (_configEntryPointer.ref.level == level.value) {
|
||||||
|
result = level;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Releases memory allocated for config entry object.
|
||||||
|
void free() => bindings.entryFree(_configEntryPointer);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ConfigEntry{name: $name, value: $value, includeDepth: $includeDepth, level: $level}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfigIterator implements Iterator<ConfigEntry> {
|
||||||
|
ConfigIterator(this._iteratorPointer);
|
||||||
|
|
||||||
|
/// Pointer to memory address for allocated config iterator.
|
||||||
|
final Pointer<git_config_iterator> _iteratorPointer;
|
||||||
|
|
||||||
|
ConfigEntry? _currentEntry;
|
||||||
|
int error = 0;
|
||||||
|
final entry = calloc<Pointer<git_config_entry>>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConfigEntry get current => _currentEntry!;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool moveNext() {
|
||||||
|
if (error < 0) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
error = libgit2.git_config_next(entry, _iteratorPointer);
|
||||||
|
if (error != -31) {
|
||||||
|
_currentEntry = ConfigEntry(entry.value);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1072,3 +1072,51 @@ class GitApplyLocation {
|
||||||
@override
|
@override
|
||||||
String toString() => 'GitApplyLocation.$_name';
|
String toString() => 'GitApplyLocation.$_name';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Priority level of a config file.
|
||||||
|
/// These priority levels correspond to the natural escalation logic
|
||||||
|
/// (from higher to lower) when searching for config entries in git.
|
||||||
|
class GitConfigLevel {
|
||||||
|
const GitConfigLevel._(this._value, this._name);
|
||||||
|
final int _value;
|
||||||
|
final String _name;
|
||||||
|
|
||||||
|
/// System-wide on Windows, for compatibility with portable git.
|
||||||
|
static const programData = GitConfigLevel._(1, 'programData');
|
||||||
|
|
||||||
|
/// System-wide configuration file; /etc/gitconfig on Linux systems.
|
||||||
|
static const system = GitConfigLevel._(2, 'system');
|
||||||
|
|
||||||
|
/// XDG compatible configuration file; typically ~/.config/git/config
|
||||||
|
static const xdg = GitConfigLevel._(3, 'xdg');
|
||||||
|
|
||||||
|
/// User-specific configuration file (also called Global configuration
|
||||||
|
/// file); typically ~/.gitconfig
|
||||||
|
static const global = GitConfigLevel._(4, 'global');
|
||||||
|
|
||||||
|
/// Repository specific configuration file; $WORK_DIR/.git/config on
|
||||||
|
/// non-bare repos.
|
||||||
|
static const local = GitConfigLevel._(5, 'local');
|
||||||
|
|
||||||
|
/// Application specific configuration file; freely defined by applications.
|
||||||
|
static const app = GitConfigLevel._(6, 'app');
|
||||||
|
|
||||||
|
/// Represents the highest level available config file (i.e. the most
|
||||||
|
/// specific config file available that actually is loaded).
|
||||||
|
static const highest = GitConfigLevel._(-1, 'highest');
|
||||||
|
|
||||||
|
static const List<GitConfigLevel> values = [
|
||||||
|
programData,
|
||||||
|
system,
|
||||||
|
xdg,
|
||||||
|
global,
|
||||||
|
local,
|
||||||
|
app,
|
||||||
|
highest,
|
||||||
|
];
|
||||||
|
|
||||||
|
int get value => _value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'GitConfigLevel.$_name';
|
||||||
|
}
|
||||||
|
|
|
@ -33,13 +33,17 @@ void main() {
|
||||||
expect(config, isA<Config>());
|
expect(config, isA<Config>());
|
||||||
});
|
});
|
||||||
|
|
||||||
test('returns map with variables and values', () {
|
test('returns config entries and their values', () {
|
||||||
expect(config.variables['remote.origin.url'], equals('someurl'));
|
expect(config.length, 5);
|
||||||
|
expect(config.last.name, 'remote.origin.url');
|
||||||
|
expect(config.last.value, 'someurl');
|
||||||
|
expect(config.last.includeDepth, 0);
|
||||||
|
expect(config.last.level, GitConfigLevel.local);
|
||||||
});
|
});
|
||||||
|
|
||||||
group('get value', () {
|
group('get value', () {
|
||||||
test('returns value of variable', () {
|
test('returns value of variable', () {
|
||||||
expect(config['core.bare'], equals('false'));
|
expect(config['core.bare'].value, 'false');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throws when variable isn\'t found', () {
|
test('throws when variable isn\'t found', () {
|
||||||
|
@ -53,25 +57,25 @@ void main() {
|
||||||
group('set value', () {
|
group('set value', () {
|
||||||
test('sets boolean value for provided variable', () {
|
test('sets boolean value for provided variable', () {
|
||||||
config['core.bare'] = true;
|
config['core.bare'] = true;
|
||||||
expect(config['core.bare'], equals('true'));
|
expect(config['core.bare'].value, 'true');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sets integer value for provided variable', () {
|
test('sets integer value for provided variable', () {
|
||||||
config['core.repositoryformatversion'] = 1;
|
config['core.repositoryformatversion'] = 1;
|
||||||
expect(config['core.repositoryformatversion'], equals('1'));
|
expect(config['core.repositoryformatversion'].value, '1');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sets string value for provided variable', () {
|
test('sets string value for provided variable', () {
|
||||||
config['remote.origin.url'] = 'updated';
|
config['remote.origin.url'] = 'updated';
|
||||||
expect(config['remote.origin.url'], equals('updated'));
|
expect(config['remote.origin.url'].value, 'updated');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('delete', () {
|
group('delete', () {
|
||||||
test('successfully deletes entry', () {
|
test('successfully deletes entry', () {
|
||||||
expect(config['core.bare'], equals('false'));
|
expect(config['core.bare'].value, 'false');
|
||||||
config.delete('core.bare');
|
config.delete('core.bare');
|
||||||
expect(config.variables['core.bare'], isNull);
|
expect(() => config['core.bare'], throwsA(isA<LibGit2Error>()));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throws on deleting non existing variable', () {
|
test('throws on deleting non existing variable', () {
|
||||||
|
@ -101,7 +105,7 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('returns empty list if multivar not found', () {
|
test('returns empty list if multivar not found', () {
|
||||||
expect(config.multivar('not.there'), equals([]));
|
expect(config.multivar('not.there'), []);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -119,7 +123,7 @@ void main() {
|
||||||
expect(multivarValues, isNot(contains('default-proxy')));
|
expect(multivarValues, isNot(contains('default-proxy')));
|
||||||
expect(multivarValues, isNot(contains('proxy-command for kernel.org')));
|
expect(multivarValues, isNot(contains('proxy-command for kernel.org')));
|
||||||
expect(multivarValues, contains('updated'));
|
expect(multivarValues, contains('updated'));
|
||||||
expect(multivarValues.length, equals(2));
|
expect(multivarValues.length, 2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -173,8 +173,10 @@ void main() {
|
||||||
|
|
||||||
test('returns config for repository', () {
|
test('returns config for repository', () {
|
||||||
final config = repo.config;
|
final config = repo.config;
|
||||||
expect(config['remote.origin.url'],
|
expect(
|
||||||
'git://github.com/SkinnyMind/libgit2dart.git');
|
config['remote.origin.url'].value,
|
||||||
|
'git://github.com/SkinnyMind/libgit2dart.git',
|
||||||
|
);
|
||||||
|
|
||||||
config.free();
|
config.free();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue