Thanks to visit codestin.com
Credit goes to github.com

Skip to content

pardnchiu/go-pkg

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-pkg

個人開發常用的 Go 工具函式集合,從過往專案中逐步累積而成。

內容

http

泛型 HTTP GET/POST/PUT/PATCH/DELETE,自動處理 JSON/XML 解碼。Stream 變體(*Stream)回傳原始 *http.Response,供需要讀 response header(如 Mcp-Session-Id)或串流 body(SSE / chunked)的場景使用。

範例
import "github.com/pardnchiu/go-pkg/http"

// GET
data, status, err := http.GET[MyStruct](ctx, nil, "https://api.example.com/data", nil)

// POST(JSON)
data, status, err := http.POST[MyStruct](ctx, nil, "https://api.example.com/data", nil, body, "json")

// POST(Form)
data, status, err := http.POST[MyStruct](ctx, nil, "https://api.example.com/data", nil, body, "form")

// PUT
data, status, err := http.PUT[MyStruct](ctx, nil, "https://api.example.com/data", nil, body, "json")

// PATCH
data, status, err := http.PATCH[MyStruct](ctx, nil, "https://api.example.com/data", nil, body, "json")

// DELETE
data, status, err := http.DELETE[MyStruct](ctx, nil, "https://api.example.com/data", nil, body, "json")

// Stream(caller 自負 defer resp.Body.Close())
resp, err := http.GETStream(ctx, nil, "https://api.example.com/sse", map[string]string{
    "Accept": "text/event-stream, application/json",
})
if err != nil {
    return err
}
defer resp.Body.Close()

sessionID := resp.Header.Get("Mcp-Session-Id")
switch ct := resp.Header.Get("Content-Type"); {
case strings.HasPrefix(ct, "text/event-stream"):
    // 逐行讀取 SSE
case strings.HasPrefix(ct, "application/json"):
    _ = json.NewDecoder(resp.Body).Decode(&result)
}

// POST/PUT/PATCH/DELETEStream 簽名與非 stream 版相同
resp, err = http.POSTStream(ctx, nil, "https://api.example.com/mcp", header, body, "json")

database

PostgreSQL 連線建構與 migration runner。

NewPostgresql 從環境變數讀取預設值(PG_DSN / PG_HOST / PG_PORT / PG_USER / PG_PASSWORD / PG_DATABASE / PG_SSLMODE),cfg 非零欄位覆寫之。

PostgresqlMigrate 遞迴掃描 dir 下所有 .sql 檔,以相對路徑為版本鍵排序執行,透過 schema_migrations 表確保冪等,每筆 migration 以 transaction 包裹。

範例
import (
	_ "github.com/lib/pq"
	"github.com/pardnchiu/go-pkg/database"
)

db, _ := database.NewPostgresql(ctx, nil)
defer db.Close()

err := database.PostgresqlMigrate(ctx, db, "./migrations")

rod(v0.13.0 起移除)

瀏覽器抓取與互動操作自 v0.13.0 起獨立至 github.com/pardnchiu/go-browser 維護,本套件不再提供 rod 子套件。請改用:

import "github.com/pardnchiu/go-browser"

歷史版本(≤ v0.12.x)仍可透過 github.com/pardnchiu/go-pkg/rod 取得;新專案請直接相依 go-browser

filesystem

檔案系統工具集(write-side + policy):

  • Policy 注入New(Policy{DeniedMap, ExcludeList}) 一次性注入 sandbox 規則(sync.Once),未注入時 IsDenied 永遠 falseIsExcluded 僅讀執行期 .gitignore回 error(既有 caller 零破壞)。IsDenied / IsExcluded / RealPath / AbsPath(root, path, AbsPathOption{HomeOnly, NeedExclude}) 為 public 工具;WriteFile / WriteText / WriteJSON / AppendText / Copy / Move / Remove 自動套 IsDenied(read-side 不套)
  • 原子寫入WriteFile / WriteText / WriteJSON / Copy 透過 .tmp + os.Rename
  • 目錄CheckDir(path, create) 檢查路徑是否為目錄(create=true 時不存在會以 0755 建立)
  • 讀寫ReadText / WriteText / AppendTextWriteJSON(path, v, format)format=true 縮排,false 緊湊)
  • 搬移Move 跨 device 自動 fallback 為 copy + remove;Remove 忽略不存在錯誤

讀取/列舉/搜尋類 API 已拆至 filesystem/reader 子套件(見下節)。

範例
import "github.com/pardnchiu/go-pkg/filesystem"

// Policy(optional,未呼叫則無 deny / exclude 限制)
err := filesystem.New(filesystem.Policy{
    DeniedMap:   deniedJSON,   // {"dirs":[...],"files":[...],"prefixes":[...],"extensions":[...]}
    ExcludeList: excludeJSON,  // []string,gitignore-like patterns
})

// Sandbox 工具
deny := filesystem.IsDenied("/etc/passwd")
excl := filesystem.IsExcluded("/work/root", "/work/root/node_modules/x.js")
real, err := filesystem.RealPath("/path/maybe/symlink")
abs, err := filesystem.AbsPath("/work/root", "~/Desktop", filesystem.AbsPathOption{
    HomeOnly:    true,
    NeedExclude: false,
})

// 原子寫入(自動套 IsDenied)
err = filesystem.WriteFile("/path/to/file.txt", "content", 0644)
err = filesystem.WriteText("/path/to/file.txt", "content")
err = filesystem.AppendText("/path/to/log.txt", "line\n")

// 目錄
err = filesystem.CheckDir("/path/to/dir", true) // 不存在則建立

// 讀取
content, err := filesystem.ReadText("/path/to/file.txt")

// JSON
type Config struct{ Host string }
err = filesystem.WriteJSON("/path/to/cfg.json", cfg, true)  // formatted
err = filesystem.WriteJSON("/path/to/cfg.json", cfg, false) // compact

// 搬移
err = filesystem.Copy("/src", "/dst")
err = filesystem.Move("/src", "/dst")               // 跨 device 自動 fallback
err = filesystem.Remove("/path/to/x")

filesystem/reader

讀取側工具:存在性檢查、列舉、遞迴 walk、glob、regex 內容搜尋。共用 variadic ListOption{SkipExcluded, IgnoreWalkError, IncludeNonRegular bool}

  • SkipExcluded=true 時依 filesystem.IsExcluded 過濾命中項,walk 對命中目錄回 filepath.SkipDir
  • IgnoreWalkError=true 僅作用於 WalkFiles / SearchFiles,walker 收到 err 時跳過該節點而不 abort 整個 walk(SearchFiles 預設為 false → halt-on-error,與 WalkFiles 一致)
  • IncludeNonRegular=true 放寬 IsRegular() 限制以收錄 symlink/device/socket/pipe(作用於 ListFiles / WalkFiles / SearchFiles
API 行為
Exists / IsFile / IsDir 包 stat 回 bool
IsEmpty(path) 區分目錄空(Readdirnames(1) EOF)與檔案 size=0
所有列舉/walking/glob/search API 統一回 []File{Name, Path, IsDir, Size, ModTime, Matches}Path 一律為 filepath.Abs 結果(Abs 失敗才 fallback 原始 path);Matchesomitempty,僅 SearchFiles 填入。
API 行為
ListFiles(dir, opts...) 非遞迴,僅 regular file
ListDirs(dir, opts...) 非遞迴,僅 dir
ListAll(dir, opts...) 非遞迴,全 type 不過濾(含 dir/symlink/device 等)
WalkFiles(root, opts...) 遞迴,僅 regular file,跳過 dot-prefix 目錄
GlobFiles(root, pattern) 支援 ** 遞迴;入口 filepath.Match 驗 pattern,malformed 即冒 ErrBadPattern
SearchFiles(root, regex, filePatterns, maxSize, opts...) regex 內容搜尋,命中檔額外填 Matches []Line{Line, Text}maxSize <= 0 fallback 為 1MB;自動跳 binary ext 與 dot-prefix
範例
import "github.com/pardnchiu/go-pkg/filesystem/reader"

// 存在性
ok := reader.Exists("/path/to/x")
ok = reader.IsFile("/path/to/x")
ok = reader.IsDir("/path/to/x")
empty, err := reader.IsEmpty("/path/to/x")

// 列舉(非遞迴)
files, err := reader.ListFiles("/path/to/dir") // []File{Name, Path, IsDir, Size, ModTime}
dirs, err := reader.ListDirs("/path/to/dir")
entries, err := reader.ListAll("/path/to/dir")

// 遞迴 walk
all, err := reader.WalkFiles("/work", reader.ListOption{SkipExcluded: true})

// 容錯:跳過 stat 失敗節點而非 abort 整個 walk
all, err = reader.WalkFiles("/work", reader.ListOption{
    SkipExcluded:    true,
    IgnoreWalkError: true,
})

// 收錄 symlink/device/socket/pipe
files, err = reader.ListFiles("/work", reader.ListOption{IncludeNonRegular: true})

// Glob(** 遞迴;回 []File)
matches, err := reader.GlobFiles("/work", "**/*.go")

// regex 內容搜尋(每檔收集所有命中行)
hits, err := reader.SearchFiles("/work", `TODO\(.*\)`, []string{"**/*.go"}, 0,
    reader.ListOption{SkipExcluded: true})
for _, h := range hits {
    // h.Path / h.Matches[i].Line / h.Matches[i].Text
}

filesystem/parser

多格式文件抽取:Markdown / PDF / DOCX / PPTX 統一回 (string, []Chunk, error) — 第一個 string 為全文、第二個為段落分塊。每個 Chunk 保留 Source / Index / Total / Content,超過 65535 rune 的段落自動以句末標點切片(中英標點皆認)。空文件回 parser.ErrEmpty sentinel,可用 errors.Is 判斷。Image / CSV / XLSX 為例外,回 (string, error)Imagedata:image/jpeg;base64,... data URL;CSV / XLSX[][]string JSON(header + 資料列)。

API 後端
Markdown(ctx, path) os.ReadFile 後依 \n\n / \r\n\r\n 分段
PDF(ctx, path) pdftotext -layout CLI(需先安裝 poppler-utils),以 \f 分頁
Docx(ctx, path) archive/zip + encoding/xml,涵蓋 word/document.xml 主文與 header* / footer* / footnotes / endnotes
PPTX(ctx, path) archive/zip + encoding/xml,按 slide(N).xml 數字排序
Image(ctx, path) image.Decode(支援 JPEG / PNG / GIF)後以 quality 85 轉 JPEG,輸出 base64 data URL
CSV(ctx, path, offset, limit) encoding/csv 讀首列為 header;.tsv 自動換 \t delimiter;BOM 自動剝除;1MB 上限;offset 為 1-indexed 起始資料列,輸出 header + 切片資料列為 [][]string JSON
XLSX(ctx, path, offset, limit) archive/zip + encoding/xml 解 OOXML;讀 xl/sharedStrings.xml 還原 SST、xl/worksheets/sheet1.xml 為首工作表;支援 s / inlineStr / b / 數字 cell;按 cell r 屬性補欄位空缺;輸出格式與 CSV
範例
import (
    "errors"
    "github.com/pardnchiu/go-pkg/filesystem/parser"
)

text, chunks, err := parser.Markdown(ctx, "./README.md")
text, chunks, err = parser.PDF(ctx, "./paper.pdf")
text, chunks, err = parser.Docx(ctx, "./report.docx")
text, chunks, err = parser.PPTX(ctx, "./slides.pptx")

dataURL, err := parser.Image(ctx, "./cover.png")        // data:image/jpeg;base64,...
rowsJSON, err := parser.CSV(ctx, "./data.csv", 1, 50)   // [["col1","col2"],["a","b"],...]
rowsJSON, err = parser.XLSX(ctx, "./data.xlsx", 1, 50)  // first sheet, same shape as CSV

if errors.Is(err, parser.ErrEmpty) {
    // 解析成功但內容為空(text 仍可能非空)
}

for _, c := range chunks {
    // c.Source / c.Index / c.Total / c.Content
}

filesystem/keychain

跨平台密鑰存取(macOS Keychain / Linux secret-tool / 檔案 fallback)。Init 一次性綁定 service 名稱與 fallback 路徑(sync.Once);Get 在 keychain 查無時 fallback 至 os.Getenv

範例
import "github.com/pardnchiu/go-pkg/filesystem/keychain"

// 初始化(sync.Once,僅首次生效)
keychain.Init("MyApp", fallbackDir)

val := keychain.Get("API_KEY")
err := keychain.Set("API_KEY", "secret")
err := keychain.Delete("API_KEY")

sandbox

跨平台子行程隔離:macOS 用 sandbox-exec(seatbelt profile)、Linux 用 bwrap(bubblewrap)。workDir 必須在 $HOME 之下(強制 + symlink 解析)。

New(deniedJSON) 一次性注入 deny dirs / files(路徑相對 home)。Wrap(ctx, binary, args, workDir, opt)*exec.Cmd,caller 自行 Run / Output

Option 欄位:

欄位 平台 說明
CPUPercent 兩者 CPU 上限(%)
MemoryMB Linux only 記憶體上限(MB);macOS 傳值會回 err
Network 兩者 NetworkAllow / NetworkDeny
DropCaps Linux only 移除所有 capability
MinimalBinds 兩者 WriteScope (WriteWork / WriteHome) + ReadOnly / ReadWrite 路徑清單

ParseMemory("512m") / ParseMemory("2GiB") 解析常見格式為 MB 整數。CheckDependence() 在 macOS 永遠 nil;Linux 檢查 bwrap 並嘗試以 apt / dnf / yum / pacman / apk 安裝。

範例
import "github.com/pardnchiu/go-pkg/sandbox"

// 一次性 deny(optional)
sandbox.New([]byte(`{"dirs":[".ssh",".aws"],"files":[".netrc"]}`))

mem, _ := sandbox.ParseMemory("1GiB") // 1024

cmd, err := sandbox.Wrap(ctx, "/usr/bin/python3", []string{"script.py"}, "/Users/me/work",
    &sandbox.Option{
        CPUPercent: 50,
        MemoryMB:   mem,
        Network:    sandbox.NetworkDeny,
        DropCaps:   true,
        MinimalBinds: &sandbox.BindSpec{
            WriteScope: sandbox.WriteWork,
            ReadOnly:   []string{"/usr", "/lib"},
        },
    })
out, err := cmd.CombinedOutput()

tui

tview / tcell primitive 統一構造器。每個 New* 函式只吃 *<Config>,所有 optional 行為(含邊框與焦點切換)皆透過 config nil-able 欄位表達。Border 非 nil 時自動 SetBorder(true)、以 " %s " 包裹標題;Border.FocusColor / Color 任一非零即綁定焦點切換(focus → FocusColor、blur → Color、初始套 Color;任一未設則 fallback ColorYellow / ColorWhite)。

API 行為
NewImage(*Image) 包裝 tview.NewImageAspectRatio > 0 才呼叫 SetAspectRatio
NewInputField(*InputField) 包裝 tview.NewInputField;label / placeholder / mask 一次設完;可選 DoneFunc / InputCapture
NewList(*List) 包裝 tview.NewList;主/副文字色與 secondary 顯示開關
NewTextView(*TextView) 包裝 tview.NewTextView;scroll / wrap / dynamic colors / regions / max lines
範例
import (
    "github.com/gdamore/tcell/v2"
    "github.com/pardnchiu/go-pkg/tui"
    "github.com/rivo/tview"
)

input := tui.NewInputField(&tui.InputField{
    Label:           "Search: ",
    LabelColor:      tcell.ColorWhite,
    FieldWidth:      40,
    PlaceholderText: "type something...",
    Border: &tui.Border{
        Title:      "Filter",
        TitleAlign: tview.AlignLeft,
        FocusColor: tcell.ColorYellow,
        Color:      tcell.ColorWhite,
    },
})

list := tui.NewList(&tui.List{
    MainTextColor:     tcell.ColorWhite,
    ShowSecondaryText: false,
    Border:            &tui.Border{Title: "Items"}, // 純標題、無焦點切換
})

text := tui.NewTextView(&tui.TextView{
    Scrollable:    true,
    Wrap:          true,
    DynamicColors: true,
})

utils

通用小工具。UUID() 產生 RFC 4122 v4 UUID。GetWithDefault / GetWithDefaultInt / GetWithDefaultFloat 讀取環境變數並於未設定、空字串或解析失敗時回傳預設值。

範例
import "github.com/pardnchiu/go-pkg/utils"

id := utils.UUID() // e.g. "f47ac10b-58cc-4372-a567-0e02b2c3d479"

host := utils.GetWithDefault("HOST", "localhost")
port := utils.GetWithDefaultInt("PORT", 8080)
ratio := utils.GetWithDefaultFloat("RATIO", 1.5)

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Contributors

Languages