// // 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 contextengine import ( "encoding/json" "fmt" "time" ) // FormatNode formats a node for display func FormatNode(node *Node, format string) map[string]interface{} { switch format { case "json": return map[string]interface{}{ "name": node.Name, "path": node.Path, "type": string(node.Type), "size": node.Size, "created_at": node.CreatedAt.Format(time.RFC3339), "updated_at": node.UpdatedAt.Format(time.RFC3339), } case "table": return map[string]interface{}{ "name": node.Name, "path": node.Path, "type": string(node.Type), "size": formatSize(node.Size), "created_at": formatTime(node.CreatedAt), "updated_at": formatTime(node.UpdatedAt), } default: // "plain" return map[string]interface{}{ "name": node.Name, "path": node.Path, "type": string(node.Type), "created_at": formatTime(node.CreatedAt), "updated_at": formatTime(node.UpdatedAt), } } } // FormatNodes formats a list of nodes for display func FormatNodes(nodes []*Node, format string) []map[string]interface{} { result := make([]map[string]interface{}, 0, len(nodes)) for _, node := range nodes { result = append(result, FormatNode(node, format)) } return result } // formatSize formats a size in bytes to human-readable format func formatSize(size int64) string { if size == 0 { return "-" } const ( KB = 1024 MB = 1024 * KB GB = 1024 * MB TB = 1024 * GB ) switch { case size >= TB: return fmt.Sprintf("%.2f TB", float64(size)/TB) case size >= GB: return fmt.Sprintf("%.2f GB", float64(size)/GB) case size >= MB: return fmt.Sprintf("%.2f MB", float64(size)/MB) case size >= KB: return fmt.Sprintf("%.2f KB", float64(size)/KB) default: return fmt.Sprintf("%d B", size) } } // formatTime formats a time to a readable string func formatTime(t time.Time) string { if t.IsZero() { return "-" } return t.Format("2006-01-02 15:04:05") } // ResultToMap converts a Result to a map for JSON serialization func ResultToMap(result *Result) map[string]interface{} { if result == nil { return map[string]interface{}{ "nodes": []interface{}{}, "total": 0, } } nodes := make([]map[string]interface{}, 0, len(result.Nodes)) for _, node := range result.Nodes { nodes = append(nodes, nodeToMap(node)) } return map[string]interface{}{ "nodes": nodes, "total": result.Total, "has_more": result.HasMore, "next_offset": result.NextOffset, } } // nodeToMap converts a Node to a map func nodeToMap(node *Node) map[string]interface{} { m := map[string]interface{}{ "name": node.Name, "path": node.Path, "type": string(node.Type), } if node.Size > 0 { m["size"] = node.Size } if !node.CreatedAt.IsZero() { m["created_at"] = node.CreatedAt.Format(time.RFC3339) } if !node.UpdatedAt.IsZero() { m["updated_at"] = node.UpdatedAt.Format(time.RFC3339) } if len(node.Metadata) > 0 { m["metadata"] = node.Metadata } return m } // MarshalJSON marshals a Result to JSON bytes func (r *Result) MarshalJSON() ([]byte, error) { return json.Marshal(ResultToMap(r)) } // PrintResult prints a result in the specified format func PrintResult(result *Result, format string) { if result == nil { fmt.Println("No results") return } switch format { case "json": data, _ := json.MarshalIndent(ResultToMap(result), "", " ") fmt.Println(string(data)) case "table": printTable(result.Nodes) default: // "plain" for _, node := range result.Nodes { fmt.Println(node.Path) } } } // printTable prints nodes in a simple table format func printTable(nodes []*Node) { if len(nodes) == 0 { fmt.Println("No results") return } // Print header fmt.Printf("%-40s %-12s %-12s %-20s %-20s\n", "NAME", "TYPE", "SIZE", "CREATED", "UPDATED") fmt.Println(string(make([]byte, 104))) // Print rows for _, node := range nodes { fmt.Printf("%-40s %-12s %-12s %-20s %-20s\n", truncateString(node.Name, 40), node.Type, formatSize(node.Size), formatTime(node.CreatedAt), formatTime(node.UpdatedAt), ) } } // truncateString truncates a string to the specified length func truncateString(s string, maxLen int) string { if len(s) <= maxLen { return s } return s[:maxLen-3] + "..." } // IsValidPath checks if a path is valid func IsValidPath(path string) bool { if path == "" { return false } // Check for invalid characters invalidChars := []string{"..", "//", "\\", "*", "?", "<", ">", "|", "\x00"} for _, char := range invalidChars { if containsString(path, char) { return false } } return true } // containsString checks if a string contains a substring func containsString(s, substr string) bool { for i := 0; i <= len(s)-len(substr); i++ { if s[i:i+len(substr)] == substr { return true } } return false } // JoinPath joins path components func JoinPath(components ...string) string { if len(components) == 0 { return "" } result := components[0] for i := 1; i < len(components); i++ { if result == "" { result = components[i] } else if components[i] == "" { continue } else { // Remove trailing slash from result for len(result) > 0 && result[len(result)-1] == '/' { result = result[:len(result)-1] } // Remove leading slash from component start := 0 for start < len(components[i]) && components[i][start] == '/' { start++ } result = result + "/" + components[i][start:] } } return result } // GetParentPath returns the parent path of a given path func GetParentPath(path string) string { path = normalizePath(path) parts := SplitPath(path) if len(parts) <= 1 { return "" } return joinStrings(parts[:len(parts)-1], "/") } // GetBaseName returns the last component of a path func GetBaseName(path string) string { path = normalizePath(path) parts := SplitPath(path) if len(parts) == 0 { return "" } return parts[len(parts)-1] } // HasPrefix checks if a path has the given prefix func HasPrefix(path, prefix string) bool { path = normalizePath(path) prefix = normalizePath(prefix) if prefix == "" { return true } if path == prefix { return true } if len(path) > len(prefix) && path[:len(prefix)+1] == prefix+"/" { return true } return false }