# 用gum给IRedis增加数据源选择能力

## 前言

[gum](https://github.com/charmbracelet/gum) 是一个命令行工具， 它可以方便地为脚本提供TUI 交互能力。 本文以扩展 [IRedis](https://iredis.xbin.io/) 为例，展示了 gum 的使用方法。

## 问题

[IRedis](https://iredis.xbin.io/) 是一个非常好用的 redis 命令行客户端， 它提供很多方便实用的功能（语法高亮，自动完成等）

要连接到指定数据源 ， 只要执行以下命令：

```sh
iredis --url 'redis://usr:passwd@foo-bar.com:6379?db=0'
```

但工作中经常需要连接不同的数据源，每次都需要输入不同的地址很麻烦。得记住每个数据库的地址和密码。 如果不能为数据源设置别名，命令行历史也无助于缓解心智负担。

```sh
iredis --url 'redis://usr:passwd@server-1.com:6379/0'
iredis --url 'redis://usr:passwd@server-2.com:6379/2'
iredis --url 'redis://:auth@server-3.com:6379'
...
```

好在IRedis 支持把数据源保存到指定配置文件中( `.iredisrc`) 中，可以为数据指定别名， 其格式如下：

```toml
[alias_dsn]
dev=redis://localhost:6379/4
staging=redis://username:password@staging-redis.example.com:6379/1
```

有了 `.iredisrc` 文件之后， 就可以用 dsn 来连接不同的数据库了

```sh
iredis --dsn dev
iredis -d staging
```

虽然这样有助于降低心智负担，但还是有点不方便： 如果忘记了dsn 的准确名字， 还是无法快速完成连接。

如果能有从 iredisrc 读取数据源列表供用户选择， 无疑可以提高效率并降低心智负担。要达成这个目标， 可以分两个步骤：

1. 解析别名
    
2. 用 gum 提供列表选择并调用iredis
    

## 解决方案

**先说解析别名**

要解析`.iredisrc` 这个 ini 格式的文件， 最简单的办法是使用python 脚本：

```python
import configparser
import os
config = configparser.ConfigParser()
config.read(os.path.expanduser('~/.iredisrc')) # read ini file
for key, _ in config.items('alias_dsn'):
  print(key) # print all key in alias_dsn
```

上述脚本解析ini 文件，并打印出 alias\_dsn 下的所有别名。

执行此脚本即可打印所有别名：

```sh
> python3 iredis.py
dev
staging
```

**用gum提供选择能力**

这时 `gum` 就可以出场了。结合前面写好的python脚本， 先读取别名列表， 用 `gum filter` 作选择， 然后用iredis打开选取的dsn

```sh
#!/usr/bin/env sh
DSN=$(python3 iredis.py | gum filter) # choose alias
iredis -d $DSN
```

最终效果如下：

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1716287417617/f6b41851-bcba-4b5d-804c-3a4de08666b4.gif align="left")

## 更多思考

其实这个模式可以应用到所有类似场景。

例如，[usql](https://github.com/xo/usql) 这个数据库 cli 几乎支持所有RDBMS， 在日常使用中也存在同样的问题。 而 `usql` 又不像 `iredis`， 它不支持在配置文件中保存数据源。

这时需要以某种形式来表达别名与数据源的对应关系， 然后传递给gum 进行选择。 遗憾的是 gum 目前不支持传入map 进行选择（虽然很久以前就有用户提出过这个[需求](https://github.com/charmbracelet/gum/issues/43)， 但被开发团队拒绝掉了）。

不过我们可以用其中提到的[思路](https://github.com/charmbracelet/gum/issues/43#issuecomment-1200303340)，用shell 来达成目标：

```sh
#!/usr/bin/env sh

# define db alias map
DBMAP=$(cat <<END
dev => pg://user:pass@localhost/dbname?sslmode=disable
staging => mysql:/var/run/mysqld/mysqld.sock
prod => sqlserver://user:pass@remote-host.com/dbname
END)

# choose alias
ALIAS=$(echo "$DBMAP" | awk -F ' => ' '{print $1}' | gum filter)

# grep dsn
DSN=$(echo "$DBMAP" | grep "$ALIAS =>" | awk -F ' => ' '{print $2}')

# open db
usql $DSN
```

这样，我们就拥行了一个可以连接任何RDBMS的统一cli 客户端，如需连接其它数据库，只需要在脚本中增加一行即可

## 结论

有了 gum 的帮助， 我们可以方便地为任何 cli 程序增加 TUI 交互能力，增加其易用性。本文只是展示了 gum 的多项强大功能中的一项， 如有兴趣， 可以到 [gum](https://github.com/charmbracelet/gum) 官网了解更多。
