diff --git a/ice/src/main/java/com/altinity/ice/cli/internal/cmd/Files.java b/ice/src/main/java/com/altinity/ice/cli/internal/cmd/Files.java index 03450a9..70f98ed 100644 --- a/ice/src/main/java/com/altinity/ice/cli/internal/cmd/Files.java +++ b/ice/src/main/java/com/altinity/ice/cli/internal/cmd/Files.java @@ -9,6 +9,7 @@ */ package com.altinity.ice.cli.internal.cmd; +import com.altinity.ice.cli.internal.util.TreePrinter; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -30,53 +31,49 @@ public static void run(RESTCatalog catalog, TableIdentifier tableId) throws IOEx Table table = catalog.loadTable(tableId); Snapshot snapshot = table.currentSnapshot(); + String tableName = tableId.toString(); + String rootLabel = "Snapshots: " + tableName; + if (snapshot == null) { - System.out.println("Snapshots: " + tableId); + System.out.println(rootLabel); System.out.println("(no snapshots)"); return; } - String tableName = tableId.toString(); int schemaId = snapshot.schemaId() != null ? snapshot.schemaId() : 0; String manifestListLocation = snapshot.manifestListLocation(); String locationStr = manifestListLocation != null ? manifestListLocation : "(embedded)"; - - System.out.println("Snapshots: " + tableName); - System.out.println( - "└── Snapshot " + snapshot.snapshotId() + ", schema " + schemaId + ": " + locationStr); + String snapshotLabel = + "Snapshot " + snapshot.snapshotId() + ", schema " + schemaId + ": " + locationStr; FileIO tableIO = table.io(); List manifests; try { manifests = snapshot.allManifests(tableIO); } catch (Exception e) { + TreePrinter.print( + new TreePrinter.Node(rootLabel, List.of(new TreePrinter.Node(snapshotLabel)))); System.out.println(" (failed to read manifests: " + e.getMessage() + ")"); return; } - for (int m = 0; m < manifests.size(); m++) { - ManifestFile manifest = manifests.get(m); - boolean isLastManifest = (m == manifests.size() - 1); - String manifestPrefix = isLastManifest ? "└── " : "├── "; - String childConnector = isLastManifest ? " " : "│ "; - - List dataFileLocations = new ArrayList<>(); + List manifestNodes = new ArrayList<>(manifests.size()); + for (ManifestFile manifest : manifests) { + List dataFileNodes = new ArrayList<>(); try (CloseableIterable files = ManifestFiles.read(manifest, tableIO)) { for (DataFile file : files) { - dataFileLocations.add(file.location()); + dataFileNodes.add(new TreePrinter.Node("Datafile: " + file.location())); } } catch (Exception e) { - dataFileLocations.add("(failed to read: " + e.getMessage() + ")"); - } - - System.out.println(" " + manifestPrefix + "Manifest: " + manifest.path()); - - String dataFileIndent = " " + childConnector; - for (int f = 0; f < dataFileLocations.size(); f++) { - boolean isLastFile = (f == dataFileLocations.size() - 1); - String filePrefix = isLastFile ? "└── " : "├── "; - System.out.println(dataFileIndent + filePrefix + "Datafile: " + dataFileLocations.get(f)); + dataFileNodes.add( + new TreePrinter.Node("Datafile: (failed to read: " + e.getMessage() + ")")); } + manifestNodes.add(new TreePrinter.Node("Manifest: " + manifest.path(), dataFileNodes)); } + + TreePrinter.Node root = + new TreePrinter.Node( + rootLabel, List.of(new TreePrinter.Node(snapshotLabel, manifestNodes))); + TreePrinter.print(root); } } diff --git a/ice/src/main/java/com/altinity/ice/cli/internal/util/TreePrinter.java b/ice/src/main/java/com/altinity/ice/cli/internal/util/TreePrinter.java new file mode 100644 index 0000000..82e87a7 --- /dev/null +++ b/ice/src/main/java/com/altinity/ice/cli/internal/util/TreePrinter.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 Altinity Inc and/or its affiliates. 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 + */ +package com.altinity.ice.cli.internal.util; + +import java.io.PrintStream; +import java.util.List; + +public final class TreePrinter { + + private TreePrinter() {} + + public record Node(String label, List children) { + public Node(String label) { + this(label, List.of()); + } + + public Node { + children = List.copyOf(children); + } + } + + public static void print(Node root) { + print(root, System.out); + } + + public static void print(Node root, PrintStream out) { + out.println(root.label()); + printChildren(root.children(), "", out); + } + + private static void printChildren(List children, String descendantIndent, PrintStream out) { + for (int i = 0; i < children.size(); i++) { + Node child = children.get(i); + boolean isLast = (i == children.size() - 1); + String connector = isLast ? "└── " : "├── "; + String childDescendantIndent = descendantIndent + (isLast ? " " : "│ "); + out.println(descendantIndent + connector + child.label()); + printChildren(child.children(), childDescendantIndent, out); + } + } +}