Skip to content

DotfileManager: add initial dotfile manager detection #1702

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions doc/json_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -978,6 +982,7 @@
"disk",
"diskio",
"de",
"dotfileManager",
"dns",
"editor",
"font",
Expand Down
1 change: 1 addition & 0 deletions presets/all.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"processes",
"packages",
"shell",
"dotfileManager",
"editor",
"display",
"brightness",
Expand Down
1 change: 1 addition & 0 deletions src/common/modules.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
38 changes: 38 additions & 0 deletions src/detection/dotfilemanager/README.md
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is frustrating. As far as I know it's commonly used by Nix community

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.
59 changes: 59 additions & 0 deletions src/detection/dotfilemanager/dotfilemanager.c
Original file line number Diff line number Diff line change
@@ -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;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should also check if git is installed

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");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if chezmoi command exists

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)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

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;
}
10 changes: 10 additions & 0 deletions src/detection/dotfilemanager/dotfilemanager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once

#include "fastfetch.h"

typedef struct FFDotfileManagerResult
{
FFstrbuf name;
} FFDotfileManagerResult;

const char* ffDetectDotfileManager(FFDotfileManagerResult* result);
114 changes: 114 additions & 0 deletions src/modules/dotfilemanager/dotfilemanager.c
Original file line number Diff line number Diff line change
@@ -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);
}
9 changes: 9 additions & 0 deletions src/modules/dotfilemanager/dotfilemanager.h
Original file line number Diff line number Diff line change
@@ -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);
11 changes: 11 additions & 0 deletions src/modules/dotfilemanager/option.h
Original file line number Diff line number Diff line change
@@ -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;
1 change: 1 addition & 0 deletions src/modules/modules.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions src/modules/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions src/options/modules.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/options/modules.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ typedef struct FFOptionsModules
FFDiskOptions disk;
FFDiskIOOptions diskIo;
FFDisplayOptions display;
FFDotfileManagerOptions dotfileManager;
FFDNSOptions dns;
FFEditorOptions editor;
FFFontOptions font;
Expand Down
Loading