From 1c6c5579d70ead982cb8d69b017d8aa1da677257 Mon Sep 17 00:00:00 2001 From: Aleksey Kulikov Date: Wed, 2 Jun 2021 20:44:58 +0300 Subject: [PATCH] feat: prototype config api --- example/config_example.dart | 17 ++ .../{example.dart => repository_example.dart} | 2 +- lib/libgit2dart.dart | 1 + lib/src/bindings/config.dart | 176 ++++++++++++++++++ lib/src/config.dart | 58 ++++++ 5 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 example/config_example.dart rename example/{example.dart => repository_example.dart} (73%) create mode 100644 lib/src/bindings/config.dart create mode 100644 lib/src/config.dart diff --git a/example/config_example.dart b/example/config_example.dart new file mode 100644 index 0000000..2502764 --- /dev/null +++ b/example/config_example.dart @@ -0,0 +1,17 @@ +import 'package:libgit2dart/libgit2dart.dart'; + +void main() { + final repoConfig = Config(path: '.git/config'); + + final isBare = repoConfig.getBool('core.bare'); + final isLoggingAllRefUpdates = repoConfig.getBool('core.logallrefupdates'); + final repoFormatVersion = repoConfig.getInt('core.repositoryformatversion'); + final remoteOriginUrl = repoConfig.getString('remote.origin.url'); + + print('Repository is bare = $isBare'); + print('Logging all ref updates = $isLoggingAllRefUpdates'); + print('Repository format version = $repoFormatVersion'); + print('Remote origin url = $remoteOriginUrl'); + + repoConfig.close(); +} diff --git a/example/example.dart b/example/repository_example.dart similarity index 73% rename from example/example.dart rename to example/repository_example.dart index 5f856f1..d5f70ed 100644 --- a/example/example.dart +++ b/example/repository_example.dart @@ -4,5 +4,5 @@ import 'package:libgit2dart/libgit2dart.dart'; void main() { final repo = Repository.open(Directory.current.path); - print(repo.path); + print('Path to git repository: ${repo.path}'); } diff --git a/lib/libgit2dart.dart b/lib/libgit2dart.dart index 9351287..ab62a5a 100644 --- a/lib/libgit2dart.dart +++ b/lib/libgit2dart.dart @@ -1,2 +1,3 @@ export 'src/repository.dart'; +export 'src/config.dart'; export 'src/error.dart'; diff --git a/lib/src/bindings/config.dart b/lib/src/bindings/config.dart new file mode 100644 index 0000000..1bff834 --- /dev/null +++ b/lib/src/bindings/config.dart @@ -0,0 +1,176 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import '../error.dart'; +import 'libgit2_bindings.dart'; +import '../util.dart'; + +/// Allocate a new configuration object +/// +/// This object is empty, so you have to add a file to it before you can do +/// anything with it. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer> newConfig() { + final out = calloc>(); + final error = libgit2.git_config_new(out); + + if (error < 0) { + throw LibGit2Error(error, libgit2.git_error_last()); + } + + return out; +} + +/// Create a new config instance containing a single on-disk file +/// +/// Throws a [LibGit2Error] if error occured. +Pointer> open(String path) { + final out = calloc>(); + final pathC = path.toNativeUtf8().cast(); + final error = libgit2.git_config_open_ondisk(out, pathC); + calloc.free(pathC); + + if (error < 0) { + throw LibGit2Error(error, libgit2.git_error_last()); + } + + return out; +} + +/// Open the global, XDG and system configuration files +/// +/// Utility wrapper that finds the global, XDG and system configuration +/// files and opens them into a single prioritized config object that can +/// be used when accessing default config data outside a repository. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer> openDefault() { + final out = calloc>(); + final error = libgit2.git_config_open_default(out); + + if (error < 0) { + throw LibGit2Error(error, libgit2.git_error_last()); + } + + return out; +} + +/// Locate the path to the global configuration file +/// +/// The user or global configuration file is usually located in +/// `$HOME/.gitconfig`. +/// +/// This method will try to guess the full path to that file, if the file +/// exists. The returned path may be used on any method call to load +/// the global configuration file. +/// +/// This method will not guess the path to the xdg compatible config file +/// (`.config/git/config`). +/// +/// Throws a [LibGit2Error] if error occured. +String findGlobal() { + final out = calloc(); + final error = libgit2.git_config_find_global(out); + final path = out.ref.ptr.cast().toDartString(); + calloc.free(out); + + if (error < 0) { + throw LibGit2Error(error, libgit2.git_error_last()); + } + + return path; +} + +/// Locate the path to the system configuration file +/// +/// If /etc/gitconfig doesn't exist, it will look for %PROGRAMFILES%\Git\etc\gitconfig +/// +/// Throws a [LibGit2Error] if error occured. +String findSystem() { + final out = calloc(); + final error = libgit2.git_config_find_system(out); + final path = out.ref.ptr.cast().toDartString(); + calloc.free(out); + + if (error < 0) { + throw LibGit2Error(error, libgit2.git_error_last()); + } + + return path; +} + +/// Locate the path to the global xdg compatible configuration file +/// +/// The xdg compatible configuration file is usually located in +/// `$HOME/.config/git/config`. +/// +/// Throws a [LibGit2Error] if error occured. +String findXdg() { + final out = calloc(); + final error = libgit2.git_config_find_xdg(out); + final path = out.ref.ptr.cast().toDartString(); + calloc.free(out); + + if (error < 0) { + throw LibGit2Error(error, libgit2.git_error_last()); + } + + return path; +} + +/// Get the value of a config variable. +/// +/// All config files will be looked into, in the order of their +/// defined level. A higher level means a higher priority. The +/// first occurrence of the variable will be returned here. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer getConfigValue(Pointer cfg, String variable) { + final out = calloc(); + final name = variable.toNativeUtf8().cast(); + final error = libgit2.git_config_get_path(out, cfg, name); + final value = out.ref.ptr; + calloc.free(out); + calloc.free(name); + + if (error < 0) { + throw LibGit2Error(error, libgit2.git_error_last()); + } + + return value; +} + +/// Get the value of a config variable and parse it as a boolean according +/// to git-config rules. +/// +/// Interprets "true", "yes", "on", 1, or any non-zero number as true. +/// Interprets "false", "no", "off", 0, or an empty string as false. +bool getBool(Pointer cfg, String variable) { + final value = getConfigValue(cfg, variable); + final out = calloc(); + libgit2.git_config_parse_bool(out, value); + final result = out.value; + calloc.free(out); + + return (result == 0) ? false : true; +} + +/// Get the value of a config variable and parse it as an integer according +/// to git-config rules. +/// +/// Handles suffixes like k, M, or G - kilo, mega, giga. +int getInt(Pointer cfg, String variable) { + final value = getConfigValue(cfg, variable); + final out = calloc(); + libgit2.git_config_parse_int64(out, value); + final result = out.value; + calloc.free(out); + + return result; +} + +/// Get the value of a config variable and parse it as a string. +String getString(Pointer cfg, String variable) { + final value = getConfigValue(cfg, variable); + return value.cast().toDartString(); +} diff --git a/lib/src/config.dart b/lib/src/config.dart new file mode 100644 index 0000000..0b54c53 --- /dev/null +++ b/lib/src/config.dart @@ -0,0 +1,58 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'bindings/libgit2_bindings.dart'; +import 'bindings/config.dart' as config; +import 'util.dart'; + +/// [Config] provides management of global configuration options +/// (system, global, XDG, excluding repository config) +class Config { + /// Initializes a new instance of [Config] class. + /// + /// If [path] isn't provided, opens global, XDG and system config files. + /// + /// [path] should point to single on-disk file; it's expected to be a native + /// Git config file following the default Git config syntax (see man git-config). + /// + /// [Config] object should be closed with [close] function to release allocated memory. + Config({this.path}) { + libgit2.git_libgit2_init(); + + if (path == null) { + configPointer = config.openDefault(); + } else { + configPointer = config.open(path!); + } + + libgit2.git_libgit2_shutdown(); + } + + /// Path to on-disk config file provided by user. + String? path; + + /// Pointer to memory address for allocated config object. + late Pointer> configPointer; + + /// Get boolean value of `key` [variable] + bool getBool(String variable) { + return config.getBool(configPointer.value, variable); + } + + ///Get integer value of `key` [variable] + int getInt(String variable) { + return config.getInt(configPointer.value, variable); + } + + ///Get string value of `key` [variable] + String getString(String variable) { + return config.getString(configPointer.value, variable); + } + + /// Set value of config key + // TODO + + /// Releases memory allocated for config object. + void close() { + calloc.free(configPointer); + } +}