Files
ragflow/internal/cli/table.go
Jin Hai 70e9743ef1 RAGFlow go API server (#13240)
# RAGFlow Go Implementation Plan 🚀

This repository tracks the progress of porting RAGFlow to Go. We'll
implement core features and provide performance comparisons between
Python and Go versions.

## Implementation Checklist

- [x] User Management APIs
- [x] Dataset Management Operations
- [x] Retrieval Test
- [x] Chat Management Operations
- [x] Infinity Go SDK

---------

Signed-off-by: Jin Hai <haijin.chn@gmail.com>
Co-authored-by: Yingfeng Zhang <yingfeng.zhang@gmail.com>
2026-03-04 19:17:16 +08:00

168 lines
4.1 KiB
Go

//
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package cli
import (
"fmt"
"strings"
"unicode"
)
// PrintTableSimple prints data in a simple table format
// Similar to Python's _print_table_simple
func PrintTableSimple(data []map[string]interface{}) {
if len(data) == 0 {
fmt.Println("No data to print")
return
}
// Collect all column names
columnSet := make(map[string]bool)
for _, item := range data {
for key := range item {
columnSet[key] = true
}
}
// Sort columns
columns := make([]string, 0, len(columnSet))
for col := range columnSet {
columns = append(columns, col)
}
// Simple sort - in production you might want specific column ordering
for i := 0; i < len(columns); i++ {
for j := i + 1; j < len(columns); j++ {
if columns[i] > columns[j] {
columns[i], columns[j] = columns[j], columns[i]
}
}
}
// Calculate column widths
colWidths := make(map[string]int)
for _, col := range columns {
maxWidth := getStringWidth(col)
for _, item := range data {
value := fmt.Sprintf("%v", item[col])
valueWidth := getStringWidth(value)
if valueWidth > maxWidth {
maxWidth = valueWidth
}
}
if maxWidth < 2 {
maxWidth = 2
}
colWidths[col] = maxWidth
}
// Generate separator
separatorParts := make([]string, 0, len(columns))
for _, col := range columns {
separatorParts = append(separatorParts, strings.Repeat("-", colWidths[col]+2))
}
separator := "+" + strings.Join(separatorParts, "+") + "+"
// Print header
fmt.Println(separator)
headerParts := make([]string, 0, len(columns))
for _, col := range columns {
headerParts = append(headerParts, fmt.Sprintf(" %-*s ", colWidths[col], col))
}
fmt.Println("|" + strings.Join(headerParts, "|") + "|")
fmt.Println(separator)
// Print data rows
for _, item := range data {
rowParts := make([]string, 0, len(columns))
for _, col := range columns {
value := fmt.Sprintf("%v", item[col])
valueWidth := getStringWidth(value)
// Truncate if too long
if valueWidth > colWidths[col] {
runes := []rune(value)
truncated := truncateString(runes, colWidths[col])
value = truncated
valueWidth = getStringWidth(value)
}
// Pad to column width
padding := colWidths[col] - valueWidth + len(value)
rowParts = append(rowParts, fmt.Sprintf(" %-*s ", padding, value))
}
fmt.Println("|" + strings.Join(rowParts, "|") + "|")
}
fmt.Println(separator)
}
// getStringWidth calculates the display width of a string
// Treats CJK characters as width 2
func getStringWidth(text string) int {
width := 0
for _, r := range text {
if isHalfWidth(r) {
width++
} else {
width += 2
}
}
return width
}
// isHalfWidth checks if a rune is half-width
func isHalfWidth(r rune) bool {
// ASCII printable characters and common whitespace
if r >= 0x20 && r <= 0x7E {
return true
}
if r == '\t' || r == '\n' || r == '\r' {
return true
}
return false
}
// truncateString truncates a string to fit within maxWidth display width
func truncateString(runes []rune, maxWidth int) string {
width := 0
for i, r := range runes {
if isHalfWidth(r) {
width++
} else {
width += 2
}
if width > maxWidth-3 {
return string(runes[:i]) + "..."
}
}
return string(runes)
}
// getMax returns the maximum of two integers
func getMax(a, b int) int {
if a > b {
return a
}
return b
}
// isWideChar checks if a character is wide (CJK, etc.)
func isWideChar(r rune) bool {
return unicode.Is(unicode.Han, r) ||
unicode.Is(unicode.Hiragana, r) ||
unicode.Is(unicode.Katakana, r) ||
unicode.Is(unicode.Hangul, r)
}