Skip to content

Commit edebb3b

Browse files
feat: ✨ allow users to configure delimiter, improve help output
1 parent 7ce113e commit edebb3b

4 files changed

Lines changed: 33 additions & 21 deletions

File tree

readme.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Configuration can be provided in multiple ways. In decreasing order of precedenc
3333

3434
### Environment variables
3535
- `RED_CONNECTION_STRING`: The Redis connection string (e.g. `redis://localhost:6379`)
36+
- `RED_DELIMITER`: The delimiter used to group keys (default: `:`)
3637

3738
### Configuration file
3839
A configuration file can be placed either in the directory where Red is executed, or in any higher-level directory, up to the user's home directory. Lower level configuration files take precedence over higher-level ones.
@@ -53,7 +54,7 @@ Use the json schema as reference for the configuration file structure. Mind the
5354
Below is an example `.redrc.json` file:
5455
```json
5556
{
56-
"$schema": "https://github.com/evertdespiegeleer/red-cli/releases/download/v0.0.5/redrc.schema.json",
57+
"$schema": "https://github.com/evertdespiegeleer/red-cli/releases/download/v0.0.8/redrc.schema.json",
5758
"connectionString": "redis://localhost:6379"
5859
}
5960
```

src/components/functional/browser/browser.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useKeyboard } from "@opentui/react";
33
import { useQuery } from "@tanstack/react-query";
44
import clipboard from "clipboardy";
55
import { useEffect, useRef, useState } from "react";
6+
import { getConfig } from "../../../config";
67
import { useRegisterKeyBind } from "../../../contexts/registered-keybinds";
78
import { getRedis } from "../../../redis";
89
import { useRoute } from "../../../routing/provider";
@@ -242,7 +243,9 @@ export function Browser(props: Props) {
242243
}
243244
if (key.name === "escape") {
244245
key.preventDefault();
245-
const newRoute = props.path.split(":").slice(0, -1).join(":");
246+
// Go up one level
247+
const { delimiter } = getConfig();
248+
const newRoute = props.path.split(delimiter).slice(0, -1).join(delimiter);
246249
setRoute(new BrowserRoute(newRoute));
247250
}
248251
});

src/config.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,12 @@ import yargs from "yargs/yargs";
66
import { z } from "zod";
77
import { version } from "./version.json";
88

9-
type ConfigOption = {
10-
shorthand?: string;
11-
};
12-
13-
const configOptions = {
14-
connectionString: {},
15-
} satisfies Record<string, ConfigOption>;
16-
17-
type ConfigOptionNames = keyof typeof configOptions;
18-
199
declare module "zod" {
2010
interface GlobalMeta {
2111
hideFromSchema?: boolean;
2212
description?: string;
2313
shorthand?: string;
14+
default?: unknown;
2415
}
2516
}
2617

@@ -33,7 +24,11 @@ export const zConfig = z.object({
3324
.meta({
3425
description: "The Redis connection string",
3526
}),
36-
} satisfies Record<ConfigOptionNames, z.ZodTypeAny>);
27+
delimiter: z.string().nonempty().default(":").meta({
28+
description: "The delimiter used to group keys",
29+
shorthand: "d",
30+
}),
31+
});
3732

3833
type Config = z.infer<typeof zConfig>;
3934

@@ -71,6 +66,8 @@ export const loadConfig = () => {
7166
let argv = yargs(hideBin(process.argv))
7267
.config(configFileConfig)
7368
.version(version)
69+
.help()
70+
.wrap(Math.min(120, process.stdout.columns - 1))
7471
.env("RED");
7572

7673
for (const [configName, configOption] of Object.entries(zConfig.shape) as [
@@ -82,6 +79,7 @@ export const loadConfig = () => {
8279
alias: metadata?.shorthand,
8380
describe: metadata?.description,
8481
type: configOption.def.innerType.type,
82+
default: configOption.def.defaultValue,
8583
});
8684
}
8785
config = zConfig.parse(argv.argv);

src/util/redis-get-grouped-keys.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getConfig } from "../config";
12
import type { getRedis } from "../redis";
23

34
export type KeyReturnType = {
@@ -8,30 +9,35 @@ export type KeyReturnType = {
89
};
910

1011
export class RedisUtils {
12+
private delimiter: string = ":";
13+
1114
constructor(
1215
private redis: ReturnType<typeof getRedis>,
1316
private path: string,
1417
) {
15-
if (path.endsWith(":")) {
16-
throw new Error("Path should not end with a colon");
18+
const { delimiter } = getConfig();
19+
this.delimiter = delimiter;
20+
if (path.endsWith(delimiter)) {
21+
throw new Error(`Path should not end with delimiter ${this.delimiter}`);
1722
}
1823
}
1924

2025
getChildKeys = (params: { depth: number }) => {
2126
const keys = new Set<KeyReturnType>();
2227
const scanStream = this.redis.scanStream({
23-
match: [this.path, "*"].filter(Boolean).join(":"),
28+
match: [this.path, "*"].filter(Boolean).join(this.delimiter),
2429
});
2530

2631
return new Promise<KeyReturnType[]>((resolve, reject) => {
2732
scanStream.on("end", () => resolve(Array.from(keys)));
2833
scanStream.on("error", (error) => reject(error));
2934
scanStream.on("data", (bunchOfKeys: string[]) => {
3035
for (const fullKey of bunchOfKeys) {
31-
const pathPrefix = this.path.length > 0 ? `${this.path}:` : "";
36+
const pathPrefix =
37+
this.path.length > 0 ? `${this.path}${this.delimiter}` : "";
3238
const relativeKey = fullKey.slice(pathPrefix.length);
3339

34-
const relativeKeySections = relativeKey.split(":");
40+
const relativeKeySections = relativeKey.split(this.delimiter);
3541

3642
if (relativeKeySections.length <= params.depth) {
3743
keys.add({
@@ -54,16 +60,20 @@ export class RedisUtils {
5460
const relevantKeys = await this.getChildKeys({ depth: params.depth + 1 });
5561
for (const key of relevantKeys) {
5662
// Remove the last section to get the group name
57-
const fullPath = key.fullPath.split(":").slice(0, -1).join(":");
58-
const pathPrefix = this.path.length > 0 ? `${this.path}:` : "";
63+
const fullPath = key.fullPath
64+
.split(this.delimiter)
65+
.slice(0, -1)
66+
.join(this.delimiter);
67+
const pathPrefix =
68+
this.path.length > 0 ? `${this.path}${this.delimiter}` : "";
5969
const relativePath = fullPath.slice(pathPrefix.length);
6070
if (relativePath === "") {
6171
continue;
6272
}
6373
groups.set(fullPath, {
6474
fullPath,
6575
relativePath,
66-
baseName: relativePath.split(":").slice(-1)[0],
76+
baseName: relativePath.split(this.delimiter).slice(-1)[0],
6777
isGroup: true,
6878
});
6979
}

0 commit comments

Comments
 (0)