staros/sysconf/csv.go
starainrt d93a851d1b
feat: 完善 staros 系统能力并更新 wincmd 发布版依赖
- 重构 sysconf 为文档模型 INI Parser 与 Config Framework
- 强化 hosts 解析、插入校验、写回与异常输入处理
- 完善 StarCmd 生命周期、等待 API、流式输出与 IO 重定向
- 扩展跨平台文件时间、文件锁、内存、进程与网络能力
- 将 Windows 进程适配更新到 b612.me/wincmd v0.1.0
- 移除本地 wincmd/win32api replace,改用发布版依赖
- 将最低 Go 版本提升到 1.18
- 补充 hosts、sysconf、FileLock、StarCmd 与平台适配回归测试
2026-06-09 18:10:19 +08:00

178 lines
4.0 KiB
Go

package sysconf
import (
"bytes"
"encoding/csv"
"errors"
"fmt"
"reflect"
"strings"
)
var ErrNilCSVValue = errors.New("nil csv value")
type CSV struct {
header []string
text [][]string
}
type CSVRow struct {
header []string
data []string
}
type CSVValue struct {
key string
value string
}
func ParseCSV(data []byte, hasHeader bool) (csvData CSV, err error) {
if len(data) == 0 {
return CSV{}, fmt.Errorf("cannot parse data,invalid data format")
}
reader := csv.NewReader(bytes.NewReader(data))
records, err := reader.ReadAll()
if err != nil {
return CSV{}, err
}
if len(records) == 0 {
return CSV{}, fmt.Errorf("cannot parse data,invalid data format")
}
start := 0
if hasHeader {
csvData.header = append([]string(nil), records[0]...)
start = 1
} else {
for i := range records[0] {
csvData.header = append(csvData.header, fmt.Sprint(i))
}
}
for _, record := range records[start:] {
if len(record) != len(csvData.header) {
return CSV{}, fmt.Errorf("cannot parse data line,got %d values but need %d", len(record), len(csvData.header))
}
csvData.text = append(csvData.text, append([]string(nil), record...))
}
return csvData, nil
}
func (csvData *CSV) Header() []string { return csvData.header }
func (csvData *CSV) Data() [][]string { return csvData.text }
func (csvData *CSV) Row(row int) *CSVRow {
if csvData == nil || row < 0 || row >= len(csvData.text) {
return nil
}
return &CSVRow{header: csvData.header, data: csvData.text[row]}
}
func (row *CSVRow) Get(key string) *CSVValue {
if row == nil {
return nil
}
for idx, header := range row.header {
if header == key {
return &CSVValue{key: key, value: row.data[idx]}
}
}
return nil
}
func (row *CSVRow) Col(key int) *CSVValue {
if row == nil || key < 0 || key >= len(row.header) {
return nil
}
return &CSVValue{key: row.header[key], value: row.data[key]}
}
func (row *CSVRow) Header() []string { return row.header }
func (csvData *CSV) MapData() []map[string]string {
var result []map[string]string
for _, record := range csvData.text {
item := make(map[string]string, len(csvData.header))
for idx, header := range csvData.header {
item[header] = record[idx]
}
result = append(result, item)
}
return result
}
func CsvAnalyse(data string) []string { return csvAnalyse(data) }
func csvAnalyse(data string) []string {
reader := csv.NewReader(strings.NewReader(data))
record, err := reader.Read()
if err != nil {
return []string{}
}
return record
}
func MarshalCSV(header []string, ins interface{}) ([]byte, error) {
v := reflect.ValueOf(ins)
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return nil, ErrNilCSVValue
}
v = v.Elem()
}
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
return nil, fmt.Errorf("not a Slice or Array")
}
rows := make([][]string, 0, v.Len())
for i := 0; i < v.Len(); i++ {
item := v.Index(i)
if item.Kind() == reflect.Ptr {
if item.IsNil() {
continue
}
item = item.Elem()
}
switch item.Kind() {
case reflect.Slice, reflect.Array:
row := make([]string, 0, item.Len())
for j := 0; j < item.Len(); j++ {
row = append(row, fmt.Sprint(item.Index(j).Interface()))
}
rows = append(rows, row)
case reflect.Struct:
row := make([]string, 0, item.NumField())
for j := 0; j < item.NumField(); j++ {
field := item.Field(j)
if !field.CanInterface() {
continue
}
row = append(row, fmt.Sprint(field.Interface()))
}
rows = append(rows, row)
}
}
width := 0
if len(header) > 0 {
width = len(header)
} else if len(rows) > 0 {
width = len(rows[0])
}
for idx, row := range rows {
if len(row) != width {
return nil, fmt.Errorf("line %d got length %d ,but need %d", idx, len(row), width)
}
}
var buf bytes.Buffer
writer := csv.NewWriter(&buf)
if len(header) > 0 {
if err := writer.Write(header); err != nil {
return nil, err
}
}
for _, row := range rows {
if err := writer.Write(row); err != nil {
return nil, err
}
}
writer.Flush()
return buf.Bytes(), writer.Error()
}