Files
fastfetch/src/common/impl/jsonconfig.c
T

352 lines
12 KiB
C
Raw Normal View History

2023-03-09 16:01:18 +08:00
#include "fastfetch.h"
2026-01-06 09:48:38 +08:00
#include "common/color.h"
#include "common/jsonconfig.h"
#include "common/printing.h"
#include "common/io.h"
#include "common/time.h"
#include "common/stringUtils.h"
#include "detection/version/version.h"
2023-06-11 00:51:29 +08:00
#include "modules/modules.h"
#include <assert.h>
#include <ctype.h>
#include <inttypes.h>
2023-03-09 16:01:18 +08:00
2026-03-29 09:28:49 +08:00
bool ffJsonConfigParseModuleArgs(yyjson_val* key, yyjson_val* val, FFModuleArgs* moduleArgs) {
if (unsafe_yyjson_equals_str(key, "type") || unsafe_yyjson_equals_str(key, "condition")) {
return true;
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
if (unsafe_yyjson_equals_str(key, "key")) {
ffStrbufSetJsonVal(&moduleArgs->key, val);
2023-03-09 16:01:18 +08:00
return true;
2026-03-29 09:28:49 +08:00
} else if (unsafe_yyjson_equals_str(key, "format")) {
ffStrbufSetJsonVal(&moduleArgs->outputFormat, val);
return true;
2026-03-29 09:28:49 +08:00
} else if (unsafe_yyjson_equals_str(key, "outputColor")) {
ffOptionParseColor(yyjson_get_str(val), &moduleArgs->outputColor);
return true;
2026-03-29 09:28:49 +08:00
} else if (unsafe_yyjson_equals_str(key, "keyColor")) {
2023-06-18 12:32:47 +08:00
ffOptionParseColor(yyjson_get_str(val), &moduleArgs->keyColor);
return true;
2026-03-29 09:28:49 +08:00
} else if (unsafe_yyjson_equals_str(key, "keyWidth")) {
moduleArgs->keyWidth = (uint32_t) yyjson_get_uint(val);
2023-03-09 16:01:18 +08:00
return true;
2026-03-29 09:28:49 +08:00
} else if (unsafe_yyjson_equals_str(key, "keyIcon")) {
ffStrbufSetJsonVal(&moduleArgs->keyIcon, val);
return true;
}
2023-03-09 16:01:18 +08:00
return false;
}
2026-03-29 09:28:49 +08:00
void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFModuleArgs* moduleArgs) {
if (moduleArgs->key.length > 0) {
yyjson_mut_obj_add_strbuf(doc, module, "key", &moduleArgs->key);
2026-03-29 09:28:49 +08:00
}
if (moduleArgs->outputFormat.length > 0) {
yyjson_mut_obj_add_strbuf(doc, module, "format", &moduleArgs->outputFormat);
2026-03-29 09:28:49 +08:00
}
if (moduleArgs->outputColor.length > 0) {
yyjson_mut_obj_add_strbuf(doc, module, "outputColor", &moduleArgs->outputColor);
2026-03-29 09:28:49 +08:00
}
if (moduleArgs->keyColor.length > 0) {
yyjson_mut_obj_add_strbuf(doc, module, "keyColor", &moduleArgs->keyColor);
2026-03-29 09:28:49 +08:00
}
if (moduleArgs->keyWidth > 0) {
yyjson_mut_obj_add_uint(doc, module, "keyWidth", moduleArgs->keyWidth);
2026-03-29 09:28:49 +08:00
}
if (moduleArgs->keyIcon.length > 0) {
yyjson_mut_obj_add_strbuf(doc, module, "keyIcon", &moduleArgs->keyIcon);
2026-03-29 09:28:49 +08:00
}
}
2026-03-29 09:28:49 +08:00
const char* ffJsonConfigParseEnum(yyjson_val* val, int* result, FFKeyValuePair pairs[]) {
if (yyjson_is_int(val)) {
2023-06-10 15:01:54 +08:00
int intVal = yyjson_get_int(val);
2023-03-10 10:56:36 +08:00
2026-03-29 09:28:49 +08:00
for (const FFKeyValuePair* pPair = pairs; pPair->key; ++pPair) {
if (intVal == pPair->value) {
2023-03-10 10:56:36 +08:00
*result = pPair->value;
return NULL;
}
}
return "Invalid enum integer";
2026-03-29 09:28:49 +08:00
} else if (yyjson_is_str(val)) {
2023-06-10 15:01:54 +08:00
const char* strVal = yyjson_get_str(val);
2026-03-29 09:28:49 +08:00
for (const FFKeyValuePair* pPair = pairs; pPair->key; ++pPair) {
if (ffStrEqualsIgnCase(strVal, pPair->key)) {
2023-03-10 10:56:36 +08:00
*result = pPair->value;
return NULL;
}
}
return "Invalid enum string";
2026-03-29 09:28:49 +08:00
} else {
2023-03-10 10:56:36 +08:00
return "Invalid enum value type; must be a string or integer";
2026-03-29 09:28:49 +08:00
}
2023-03-10 10:56:36 +08:00
}
2023-06-11 00:51:29 +08:00
2026-03-29 09:28:49 +08:00
static bool parseModuleJsonObject(const char* type, yyjson_val* jsonVal, yyjson_mut_doc* jsonDoc) {
if (!ffCharIsEnglishAlphabet(type[0])) {
return false;
}
2023-06-11 00:51:29 +08:00
2026-03-29 09:28:49 +08:00
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(type[0]) - 'A']; *modules; ++modules) {
2023-08-20 21:35:38 +08:00
FFModuleBaseInfo* baseInfo = *modules;
2026-03-29 09:28:49 +08:00
if (ffStrEqualsIgnCase(type, baseInfo->name)) {
uint8_t optionBuf[FF_OPTION_MAX_SIZE];
baseInfo->initOptions(optionBuf);
2026-03-29 09:28:49 +08:00
if (jsonVal) {
baseInfo->parseJsonObject(optionBuf, jsonVal);
}
bool succeeded;
2026-03-29 09:28:49 +08:00
if (jsonDoc) {
yyjson_mut_val* module = yyjson_mut_arr_add_obj(jsonDoc, jsonDoc->root);
yyjson_mut_obj_add_str(jsonDoc, module, "type", baseInfo->name);
2026-03-29 09:28:49 +08:00
if (baseInfo->generateJsonResult) {
succeeded = baseInfo->generateJsonResult(optionBuf, jsonDoc, module);
2026-03-29 09:28:49 +08:00
} else {
yyjson_mut_obj_add_str(jsonDoc, module, "error", "Unsupported for JSON format");
succeeded = false;
}
2026-03-29 09:28:49 +08:00
} else {
succeeded = baseInfo->printModule(optionBuf);
2026-03-29 09:28:49 +08:00
}
baseInfo->destroyOptions(optionBuf);
return succeeded;
2023-06-11 00:51:29 +08:00
}
}
2026-03-29 09:28:49 +08:00
if (jsonDoc) {
yyjson_mut_val* module = yyjson_mut_arr_add_obj(jsonDoc, jsonDoc->root);
yyjson_mut_obj_add_strcpy(jsonDoc, module, "type", type);
yyjson_mut_obj_add_str(jsonDoc, module, "error", "Unknown module type");
2026-03-29 09:28:49 +08:00
} else {
FFModuleArgs moduleArgs;
ffOptionInitModuleArg(&moduleArgs, "");
ffPrintError(type, 0, &moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown module type");
ffOptionDestroyModuleArg(&moduleArgs);
}
2023-08-20 21:35:38 +08:00
return false;
2023-06-11 00:51:29 +08:00
}
2026-03-29 09:28:49 +08:00
static void prepareModuleJsonObject(const char* type, yyjson_val* module) {
switch (type[0]) {
case 'c':
case 'C': {
if (ffStrEqualsIgnCase(type, FF_CPUUSAGE_MODULE_NAME)) {
ffPrepareCPUUsage();
} else if (ffStrEqualsIgnCase(type, FF_COMMAND_MODULE_NAME)) {
2026-04-09 16:20:59 +08:00
FF_A_CLEANUP(ffDestroyCommandOptions) FFCommandOptions options;
ffInitCommandOptions(&options);
2026-03-29 09:28:49 +08:00
if (module) {
ffCommandModuleInfo.parseJsonObject(&options, module);
}
ffPrepareCommand(&options);
}
break;
}
2026-03-29 09:28:49 +08:00
case 'd':
case 'D': {
if (ffStrEqualsIgnCase(type, FF_DISKIO_MODULE_NAME)) {
2026-04-09 16:20:59 +08:00
FF_A_CLEANUP(ffDestroyDiskIOOptions) FFDiskIOOptions options;
ffInitDiskIOOptions(&options);
2026-03-29 09:28:49 +08:00
if (module) {
ffDiskIOModuleInfo.parseJsonObject(&options, module);
}
ffPrepareDiskIO(&options);
2023-10-08 16:30:09 +08:00
}
break;
}
2026-03-29 09:28:49 +08:00
case 'n':
case 'N': {
if (ffStrEqualsIgnCase(type, FF_NETIO_MODULE_NAME)) {
2026-04-09 16:20:59 +08:00
FF_A_CLEANUP(ffDestroyNetIOOptions) FFNetIOOptions options;
ffInitNetIOOptions(&options);
2026-03-29 09:28:49 +08:00
if (module) {
ffNetIOModuleInfo.parseJsonObject(&options, module);
}
ffPrepareNetIO(&options);
2023-09-26 22:34:37 +08:00
}
break;
}
2026-03-29 09:28:49 +08:00
case 'p':
case 'P': {
if (ffStrEqualsIgnCase(type, FF_PUBLICIP_MODULE_NAME)) {
2026-04-09 16:20:59 +08:00
FF_A_CLEANUP(ffDestroyPublicIpOptions) FFPublicIPOptions options;
ffInitPublicIpOptions(&options);
2026-03-29 09:28:49 +08:00
if (module) {
ffPublicIPModuleInfo.parseJsonObject(&options, module);
}
ffPreparePublicIp(&options);
}
break;
}
2026-03-29 09:28:49 +08:00
case 'w':
case 'W': {
if (ffStrEqualsIgnCase(type, FF_WEATHER_MODULE_NAME)) {
2026-04-09 16:20:59 +08:00
FF_A_CLEANUP(ffDestroyWeatherOptions) FFWeatherOptions options;
ffInitWeatherOptions(&options);
2026-03-29 09:28:49 +08:00
if (module) {
ffWeatherModuleInfo.parseJsonObject(&options, module);
}
ffPrepareWeather(&options);
}
break;
}
}
}
2026-03-29 09:28:49 +08:00
static bool matchesJsonArray(const char* str, yyjson_val* val) {
assert(val);
2026-03-29 09:28:49 +08:00
if (unsafe_yyjson_is_str(val)) {
return ffStrEqualsIgnCase(str, unsafe_yyjson_get_str(val));
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
if (!unsafe_yyjson_is_arr(val)) {
return false;
}
size_t idx, max;
yyjson_val* item;
2026-03-29 09:28:49 +08:00
yyjson_arr_foreach (val, idx, max, item) {
if (yyjson_is_str(item) && ffStrEqualsIgnCase(str, unsafe_yyjson_get_str(item))) {
return true;
2026-03-29 09:28:49 +08:00
}
}
return false;
}
2026-03-29 09:28:49 +08:00
static const char* printJsonConfig(FFdata* data, bool prepare) {
yyjson_mut_doc* jsonDoc = data->resultDoc;
yyjson_val* const root = yyjson_doc_get_root(data->configDoc);
2023-06-11 00:51:29 +08:00
assert(root);
2026-03-29 09:28:49 +08:00
if (!yyjson_is_obj(root)) {
2023-06-11 00:51:29 +08:00
return "Invalid JSON config format. Root value must be an object";
2026-03-29 09:28:49 +08:00
}
2023-06-11 00:51:29 +08:00
yyjson_val* modules = yyjson_obj_get(root, "modules");
2026-03-29 09:28:49 +08:00
if (!modules) {
return NULL;
}
if (!yyjson_is_arr(modules)) {
return "Property 'modules' must be an array of strings or objects";
}
2023-06-11 00:51:29 +08:00
bool succeeded = true;
int32_t thres = instance.config.display.stat;
2023-06-11 00:51:29 +08:00
yyjson_val* item;
size_t idx, max;
2026-03-29 09:28:49 +08:00
yyjson_arr_foreach (modules, idx, max, item) {
2024-08-12 11:03:52 +08:00
double ms = 0;
2026-03-29 09:28:49 +08:00
if (!prepare && thres >= 0) {
2023-06-11 00:51:29 +08:00
ms = ffTimeGetTick();
2026-03-29 09:28:49 +08:00
}
2023-06-11 00:51:29 +08:00
yyjson_val* module = item;
const char* type = yyjson_get_str(module);
2026-03-29 09:28:49 +08:00
if (type) {
2023-06-11 00:51:29 +08:00
module = NULL;
2026-03-29 09:28:49 +08:00
} else if (yyjson_is_obj(module)) {
yyjson_val* conditions = yyjson_obj_get(module, "condition");
2026-03-29 09:28:49 +08:00
if (conditions) {
if (!yyjson_is_obj(conditions)) {
return "Property 'condition' must be an object";
2026-03-29 09:28:49 +08:00
}
yyjson_val* system = yyjson_obj_get(conditions, "system");
2026-03-29 09:28:49 +08:00
if (system && !matchesJsonArray(ffVersionResult.sysName, system)) {
continue;
2026-03-29 09:28:49 +08:00
}
system = yyjson_obj_get(conditions, "!system");
2026-03-29 09:28:49 +08:00
if (system && matchesJsonArray(ffVersionResult.sysName, system)) {
continue;
2026-03-29 09:28:49 +08:00
}
yyjson_val* arch = yyjson_obj_get(conditions, "arch");
2026-03-29 09:28:49 +08:00
if (arch && !matchesJsonArray(ffVersionResult.architecture, arch)) {
continue;
2026-03-29 09:28:49 +08:00
}
arch = yyjson_obj_get(conditions, "!arch");
2026-03-29 09:28:49 +08:00
if (arch && matchesJsonArray(ffVersionResult.architecture, arch)) {
continue;
2026-03-29 09:28:49 +08:00
}
yyjson_val* previousSucceeded = yyjson_obj_get(conditions, "succeeded");
2026-03-29 09:28:49 +08:00
if (previousSucceeded && !unsafe_yyjson_is_null(previousSucceeded)) {
if (!unsafe_yyjson_is_bool(previousSucceeded)) {
return "Property 'succeeded' in 'condition' must be a boolean";
2026-03-29 09:28:49 +08:00
}
if (succeeded != unsafe_yyjson_get_bool(previousSucceeded)) {
continue;
2026-03-29 09:28:49 +08:00
}
}
}
2023-06-11 00:51:29 +08:00
type = yyjson_get_str(yyjson_obj_get(module, "type"));
2026-03-29 09:28:49 +08:00
if (!type) {
return "module object must contain a \"type\" key ( case sensitive )";
}
if (yyjson_obj_size(module) == 1) { // contains only Property type
2023-06-11 00:51:29 +08:00
module = NULL;
2026-03-29 09:28:49 +08:00
}
} else {
2023-06-11 00:51:29 +08:00
return "modules must be an array of strings or objects";
2026-03-29 09:28:49 +08:00
}
2023-06-11 00:51:29 +08:00
2026-03-29 09:28:49 +08:00
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, type, ':')) {
2026-01-15 15:11:08 +08:00
continue;
2026-03-29 09:28:49 +08:00
}
2026-01-15 15:11:08 +08:00
2026-03-29 09:28:49 +08:00
if (prepare) {
prepareModuleJsonObject(type, module);
2026-03-29 09:28:49 +08:00
} else {
succeeded = parseModuleJsonObject(type, module, jsonDoc);
2026-03-29 09:28:49 +08:00
}
2023-06-11 00:51:29 +08:00
2026-03-29 09:28:49 +08:00
if (!prepare && thres >= 0) {
2023-08-29 11:13:55 +08:00
ms = ffTimeGetTick() - ms;
2026-03-29 09:28:49 +08:00
if (jsonDoc) {
2023-10-26 15:39:28 +08:00
yyjson_mut_val* moduleJson = yyjson_mut_arr_get_last(jsonDoc->root);
2024-08-12 11:03:52 +08:00
yyjson_mut_obj_add_real(jsonDoc, moduleJson, "stat", ms);
2026-03-29 09:28:49 +08:00
} else {
2024-08-12 16:16:47 +08:00
char str[64];
2024-08-12 11:03:52 +08:00
int len = snprintf(str, sizeof str, "%.3fms", ms);
2026-03-29 09:28:49 +08:00
if (thres > 0) {
snprintf(str, sizeof str, "\e[%sm%.3fms\e[m", (ms <= thres ? FF_COLOR_FG_GREEN : ms <= 2 * thres ? FF_COLOR_FG_YELLOW
: FF_COLOR_FG_RED),
ms);
}
2025-05-16 10:33:33 +08:00
printf("\e7\e[1A\e[9999999C\e[%dD%s\e8", len, str); // Save; Up 1; Right 9999999; Left <len>; Print <str>; Load
2023-08-29 11:13:55 +08:00
}
2023-06-11 00:51:29 +08:00
}
2026-03-29 09:28:49 +08:00
#if defined(_WIN32)
if (!instance.config.display.noBuffer && !jsonDoc) {
fflush(stdout);
}
#endif
2023-06-11 00:51:29 +08:00
}
return NULL;
}
2026-03-29 09:28:49 +08:00
void ffPrintJsonConfig(FFdata* data, bool prepare) {
yyjson_mut_doc* jsonDoc = data->resultDoc;
const char* error = printJsonConfig(data, prepare);
2026-03-29 09:28:49 +08:00
if (error) {
if (jsonDoc) {
2023-10-26 15:39:28 +08:00
yyjson_mut_val* obj = yyjson_mut_obj(jsonDoc);
yyjson_mut_obj_add_str(jsonDoc, obj, "error", error);
yyjson_mut_doc_set_root(jsonDoc, obj);
2026-03-29 09:28:49 +08:00
} else {
ffPrintError("JsonConfig", 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "%s", error);
2026-03-29 09:28:49 +08:00
}
2023-10-26 15:39:28 +08:00
}
2023-06-11 00:51:29 +08:00
}