From 743a936e715807f8d9c77df4543a45276b571c4b Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Apr 2025 15:48:16 +0000 Subject: [PATCH] DotfileManager: add initial dotfile manager detection --- CMakeLists.txt | 2 + doc/json_schema.json | 5 + presets/all.jsonc | 1 + src/common/modules.c | 1 + src/detection/dotfilemanager/README.md | 38 ++++++ src/detection/dotfilemanager/dotfilemanager.c | 59 +++++++++ src/detection/dotfilemanager/dotfilemanager.h | 10 ++ src/modules/dotfilemanager/dotfilemanager.c | 114 ++++++++++++++++++ src/modules/dotfilemanager/dotfilemanager.h | 9 ++ src/modules/dotfilemanager/option.h | 11 ++ src/modules/modules.h | 1 + src/modules/options.h | 1 + src/options/modules.c | 2 + src/options/modules.h | 1 + 14 files changed, 255 insertions(+) create mode 100644 src/detection/dotfilemanager/README.md create mode 100644 src/detection/dotfilemanager/dotfilemanager.c create mode 100644 src/detection/dotfilemanager/dotfilemanager.h create mode 100644 src/modules/dotfilemanager/dotfilemanager.c create mode 100644 src/modules/dotfilemanager/dotfilemanager.h create mode 100644 src/modules/dotfilemanager/option.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b85e140e73..1e765a8a49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -376,6 +376,7 @@ set(LIBFASTFETCH_SRC src/detection/disk/disk.c src/detection/diskio/diskio.c src/detection/displayserver/displayserver.c + src/detection/dotfilemanager/dotfilemanager.c src/detection/editor/editor.c src/detection/font/font.c src/detection/gpu/gpu.c @@ -420,6 +421,7 @@ set(LIBFASTFETCH_SRC src/modules/de/de.c src/modules/disk/disk.c src/modules/diskio/diskio.c + src/modules/dotfilemanager/dotfilemanager.c src/modules/dns/dns.c src/modules/editor/editor.c src/modules/font/font.c diff --git a/doc/json_schema.json b/doc/json_schema.json index b6ad80b0a4..56e10ec36f 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -190,6 +190,10 @@ "description": "Output format of the module `DiskIO`. See Wiki for formatting syntax\n 1. {size-read}: Size of data read [per second] (formatted)\n 2. {size-written}: Size of data written [per second] (formatted)\n 3. {name}: Device name\n 4. {dev-path}: Device raw file path\n 5. {bytes-read}: Size of data read [per second] (in bytes)\n 6. {bytes-written}: Size of data written [per second] (in bytes)\n 7. {read-count}: Number of reads\n 8. {write-count}: Number of writes", "type": "string" }, + "dotfileManagerFormat": { + "description": "Output format of the module `DotfileManager`. See Wiki for formatting syntax\n 1. {name}: Name", + "type": "string" + }, "dnsFormat": { "description": "Output format of the module `DNS`. See Wiki for formatting syntax\n 1. {result}: DNS result", "type": "string" @@ -978,6 +982,7 @@ "disk", "diskio", "de", + "dotfileManager", "dns", "editor", "font", diff --git a/presets/all.jsonc b/presets/all.jsonc index d1e30ef4c5..4960f273df 100644 --- a/presets/all.jsonc +++ b/presets/all.jsonc @@ -21,6 +21,7 @@ "processes", "packages", "shell", + "dotfileManager", "editor", "display", "brightness", diff --git a/src/common/modules.c b/src/common/modules.c index 724c1aa493..5b6483f688 100644 --- a/src/common/modules.c +++ b/src/common/modules.c @@ -36,6 +36,7 @@ static FFModuleBaseInfo* D[] = { (FFModuleBaseInfo*) &instance.config.modules.display, (FFModuleBaseInfo*) &instance.config.modules.disk, (FFModuleBaseInfo*) &instance.config.modules.diskIo, + (FFModuleBaseInfo*) &instance.config.modules.dotfileManager, (FFModuleBaseInfo*) &instance.config.modules.dns, NULL, }; diff --git a/src/detection/dotfilemanager/README.md b/src/detection/dotfilemanager/README.md new file mode 100644 index 0000000000..5dd70c0b50 --- /dev/null +++ b/src/detection/dotfilemanager/README.md @@ -0,0 +1,38 @@ +# Dotfile Manager + +This module attempts to detect the user's dotfile manager. Many [popular dotfile +managers](https://dotfiles.github.io/utilities/) are supported. + +## Supported dotfile managers + +* [Bare git repository](https://www.atlassian.com/git/tutorials/dotfiles) +* [chezmoi](https://chezmoi.io) +* [YADM](https://yadm.io) + +## Unsupported dotfile managers + +### Dotbot + +As [Dotbot](https://github.com/anishathalye/dotbot) is usually imported as a +submodule inside the user's dotfile repository, there is no reliable way to +detect if the user is using it. + +### GNU Stow + +[GNU Stow](https://www.gnu.org/software/stow/) has no standard configuration, so +there is no reliable way to detect if the user is using it. + +### Home manager + +[Home manager](https://github.com/nix-community/home-manager) has no standard +configuration. + +### Mackup + +[Mackup](https://github.com/lra/mackup) does not work on macOS since version 14 +(Sonoma, released September 2023). + +### rcm + +[rcm](https://thoughtbot.github.io/rcm/rcm.7.html) has no standard +configuration. diff --git a/src/detection/dotfilemanager/dotfilemanager.c b/src/detection/dotfilemanager/dotfilemanager.c new file mode 100644 index 0000000000..b5e294c309 --- /dev/null +++ b/src/detection/dotfilemanager/dotfilemanager.c @@ -0,0 +1,59 @@ +#include "common/io/io.h" +#include "dotfilemanager.h" + +static bool detectBareGitRepository(FFDotfileManagerResult* result) +{ + FF_STRBUF_AUTO_DESTROY fullPath = ffStrbufCreate(); + ffStrbufSet(&fullPath, &instance.state.platform.homeDir); + ffStrbufAppendS(&fullPath, ".git"); + + if (ffPathExists(fullPath.chars, FF_PATHTYPE_DIRECTORY)) { + ffStrbufSetS(&result->name, "bare git repository"); + return true; + } + + return false; +} + +static bool detectChezmoi(FFDotfileManagerResult* result) +{ + FF_LIST_FOR_EACH(FFstrbuf, dataDir, instance.state.platform.dataDirs) + { + FF_STRBUF_AUTO_DESTROY fullPath = ffStrbufCreate(); + ffStrbufSet(&fullPath, dataDir); + ffStrbufAppendS(&fullPath, "chezmoi"); + + if (ffPathExists(fullPath.chars, FF_PATHTYPE_DIRECTORY)) { + ffStrbufSetS(&result->name, "chezmoi"); + return true; + } + } + + return false; +} + +static bool detectYADM(FFDotfileManagerResult* result) +{ + FF_LIST_FOR_EACH(FFstrbuf, configDir, instance.state.platform.configDirs) + { + FF_STRBUF_AUTO_DESTROY fullPath = ffStrbufCreate(); + ffStrbufSet(&fullPath, configDir); + ffStrbufAppendS(&fullPath, "yadm"); + + if (ffPathExists(fullPath.chars, FF_PATHTYPE_DIRECTORY)) { + ffStrbufSetS(&result->name, "yadm"); + return true; + } + } + + return false; +} + +const char* ffDetectDotfileManager(FFDotfileManagerResult* result) +{ + if (detectBareGitRepository(result)) return NULL; + if (detectChezmoi(result)) return NULL; + if (detectYADM(result)) return NULL; + + return NULL; +} diff --git a/src/detection/dotfilemanager/dotfilemanager.h b/src/detection/dotfilemanager/dotfilemanager.h new file mode 100644 index 0000000000..2c60e87b6e --- /dev/null +++ b/src/detection/dotfilemanager/dotfilemanager.h @@ -0,0 +1,10 @@ +#pragma once + +#include "fastfetch.h" + +typedef struct FFDotfileManagerResult +{ + FFstrbuf name; +} FFDotfileManagerResult; + +const char* ffDetectDotfileManager(FFDotfileManagerResult* result); diff --git a/src/modules/dotfilemanager/dotfilemanager.c b/src/modules/dotfilemanager/dotfilemanager.c new file mode 100644 index 0000000000..4d5de35740 --- /dev/null +++ b/src/modules/dotfilemanager/dotfilemanager.c @@ -0,0 +1,114 @@ +#include "common/printing.h" +#include "common/jsonconfig.h" +#include "detection/libc/libc.h" +#include "detection/dotfilemanager/dotfilemanager.h" +#include "modules/dotfilemanager/dotfilemanager.h" +#include "util/stringUtils.h" + +void ffPrintDotfileManager(FFDotfileManagerOptions* options) +{ + FFDotfileManagerResult result = { + .name = ffStrbufCreate(), + }; + const char* error = ffDetectDotfileManager(&result); + + if (error) + { + ffPrintError(FF_DOTFILEMANAGER_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + return; + } + + if (options->moduleArgs.outputFormat.length == 0) + { + ffPrintLogoAndKey(FF_DOTFILEMANAGER_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffStrbufWriteTo(&result.name, stdout); + putchar('\n'); + } + else + { + FF_PRINT_FORMAT_CHECKED(FF_DOTFILEMANAGER_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]){ + FF_FORMAT_ARG(result.name, "name"), + })); + } + + ffStrbufDestroy(&result.name); +} + +bool ffParseDotfileManagerCommandOptions(FFDotfileManagerOptions* options, const char* key, const char* value) +{ + const char* subKey = ffOptionTestPrefix(key, FF_DOTFILEMANAGER_MODULE_NAME); + if (!subKey) return false; + if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) + return true; + + return false; +} + +void ffParseDotfileManagerJsonObject(FFDotfileManagerOptions* options, yyjson_val* module) +{ + yyjson_val *key_, *val; + size_t idx, max; + yyjson_obj_foreach(module, idx, max, key_, val) + { + const char* key = yyjson_get_str(key_); + if(ffStrEqualsIgnCase(key, "type")) + continue; + + if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) + continue; + + ffPrintError(FF_DOTFILEMANAGER_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", key); + } +} + +void ffGenerateDotfileManagerJsonConfig(FFDotfileManagerOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + __attribute__((__cleanup__(ffDestroyDotfileManagerOptions))) FFDotfileManagerOptions defaultOptions; + ffInitDotfileManagerOptions(&defaultOptions); + + ffJsonConfigGenerateModuleArgsConfig(doc, module, &defaultOptions.moduleArgs, &options->moduleArgs); +} + +void ffGenerateDotfileManagerJsonResult(FF_MAYBE_UNUSED FFDotfileManagerOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFDotfileManagerResult result = { + .name = ffStrbufCreate(), + }; + + const char* error = ffDetectDotfileManager(&result); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &result.name); + + ffStrbufDestroy(&result.name); +} + +static FFModuleBaseInfo ffModuleInfo = { + .name = FF_DOTFILEMANAGER_MODULE_NAME, + .description = "Print information about the dotfile manager", + .parseCommandOptions = (void*) ffParseDotfileManagerCommandOptions, + .parseJsonObject = (void*) ffParseDotfileManagerJsonObject, + .printModule = (void*) ffPrintDotfileManager, + .generateJsonResult = (void*) ffGenerateDotfileManagerJsonResult, + .generateJsonConfig = (void*) ffGenerateDotfileManagerJsonConfig, + .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) { + {"Name", "name"}, + })) +}; + +void ffInitDotfileManagerOptions(FFDotfileManagerOptions* options) +{ + options->moduleInfo = ffModuleInfo; + ffOptionInitModuleArg(&options->moduleArgs, "󰣫"); +} + +void ffDestroyDotfileManagerOptions(FFDotfileManagerOptions* options) +{ + ffOptionDestroyModuleArg(&options->moduleArgs); +} diff --git a/src/modules/dotfilemanager/dotfilemanager.h b/src/modules/dotfilemanager/dotfilemanager.h new file mode 100644 index 0000000000..479acaf564 --- /dev/null +++ b/src/modules/dotfilemanager/dotfilemanager.h @@ -0,0 +1,9 @@ +#pragma once + +#include "fastfetch.h" + +#define FF_DOTFILEMANAGER_MODULE_NAME "DotfileManager" + +void ffPrintDotfileManager(FFDotfileManagerOptions* options); +void ffInitDotfileManagerOptions(FFDotfileManagerOptions* options); +void ffDestroyDotfileManagerOptions(FFDotfileManagerOptions* options); diff --git a/src/modules/dotfilemanager/option.h b/src/modules/dotfilemanager/option.h new file mode 100644 index 0000000000..35c5c77c15 --- /dev/null +++ b/src/modules/dotfilemanager/option.h @@ -0,0 +1,11 @@ +#pragma once + +// This file will be included in "fastfetch.h", do NOT put unnecessary things here + +#include "common/option.h" + +typedef struct FFDotfileManagerOptions +{ + FFModuleBaseInfo moduleInfo; + FFModuleArgs moduleArgs; +} FFDotfileManagerOptions; diff --git a/src/modules/modules.h b/src/modules/modules.h index e6594f0309..fba658badb 100644 --- a/src/modules/modules.h +++ b/src/modules/modules.h @@ -25,6 +25,7 @@ #include "modules/diskio/diskio.h" #include "modules/display/display.h" #include "modules/de/de.h" +#include "modules/dotfilemanager/dotfilemanager.h" #include "modules/dns/dns.h" #include "modules/editor/editor.h" #include "modules/font/font.h" diff --git a/src/modules/options.h b/src/modules/options.h index 7cf98c1274..df838d6309 100644 --- a/src/modules/options.h +++ b/src/modules/options.h @@ -25,6 +25,7 @@ #include "modules/disk/option.h" #include "modules/diskio/option.h" #include "modules/display/option.h" +#include "modules/dotfilemanager/option.h" #include "modules/dns/option.h" #include "modules/editor/option.h" #include "modules/font/option.h" diff --git a/src/options/modules.c b/src/options/modules.c index 755f65a87a..18cd2ff0a4 100644 --- a/src/options/modules.c +++ b/src/options/modules.c @@ -26,6 +26,7 @@ void ffOptionsInitModules(FFOptionsModules* options) ffInitDiskOptions(&options->disk); ffInitDiskIOOptions(&options->diskIo); ffInitDisplayOptions(&options->display); + ffInitDotfileManagerOptions(&options->dotfileManager); ffInitDNSOptions(&options->dns); ffInitEditorOptions(&options->editor); ffInitFontOptions(&options->font); @@ -103,6 +104,7 @@ void ffOptionsDestroyModules(FFOptionsModules* options) ffDestroyDiskOptions(&options->disk); ffDestroyDiskIOOptions(&options->diskIo); ffDestroyDisplayOptions(&options->display); + ffDestroyDotfileManagerOptions(&options->dotfileManager); ffDestroyDNSOptions(&options->dns); ffDestroyEditorOptions(&options->editor); ffDestroyFontOptions(&options->font); diff --git a/src/options/modules.h b/src/options/modules.h index 4ff82f972f..8075e621fc 100644 --- a/src/options/modules.h +++ b/src/options/modules.h @@ -27,6 +27,7 @@ typedef struct FFOptionsModules FFDiskOptions disk; FFDiskIOOptions diskIo; FFDisplayOptions display; + FFDotfileManagerOptions dotfileManager; FFDNSOptions dns; FFEditorOptions editor; FFFontOptions font;