io.trino.cli.FormatUtils Maven / Gradle / Ivy
/*
* 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 io.trino.cli;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.primitives.Ints;
import io.airlift.units.DataSize;
import io.airlift.units.Duration;
import io.trino.client.Row;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.List;
import java.util.Map;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Strings.repeat;
import static com.google.common.collect.Iterables.partition;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.io.BaseEncoding.base16;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.String.format;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.stream.Collectors.joining;
public final class FormatUtils
{
private static final Splitter HEX_SPLITTER = Splitter.fixedLength(2);
private static final Joiner HEX_BYTE_JOINER = Joiner.on(' ');
private static final Joiner HEX_LINE_JOINER = Joiner.on('\n');
private FormatUtils() {}
public static String formatCount(long count)
{
double fractional = count;
String unit = "";
if (fractional > 1000) {
fractional /= 1000;
unit = "K";
}
if (fractional > 1000) {
fractional /= 1000;
unit = "M";
}
if (fractional > 1000) {
fractional /= 1000;
unit = "B";
}
if (fractional > 1000) {
fractional /= 1000;
unit = "T";
}
if (fractional > 1000) {
fractional /= 1000;
unit = "Q";
}
return format("%s%s", getFormat(fractional).format(fractional), unit);
}
public static String formatCountRate(double count, Duration duration, boolean longForm)
{
double rate = count / duration.getValue(SECONDS);
if (Double.isNaN(rate) || Double.isInfinite(rate)) {
rate = 0;
}
String rateString = formatCount((long) rate);
if (longForm) {
if (rateString.endsWith(" ")) {
rateString = rateString.substring(0, rateString.length() - 1);
}
rateString += "/s";
}
return rateString;
}
public static String formatDataSize(DataSize size, boolean longForm, boolean decimalDataSize)
{
int divisor = decimalDataSize ? 1000 : 1024;
double fractional = size.toBytes();
String unit = null;
if (fractional >= divisor) {
fractional /= divisor;
unit = "K";
}
if (fractional >= divisor) {
fractional /= divisor;
unit = "M";
}
if (fractional >= divisor) {
fractional /= divisor;
unit = "G";
}
if (fractional >= divisor) {
fractional /= divisor;
unit = "T";
}
if (fractional >= divisor) {
fractional /= divisor;
unit = "P";
}
if (unit == null) {
unit = "B";
}
else {
if (!decimalDataSize) {
unit += "i";
}
if (longForm) {
unit += "B";
}
}
return format("%s%s", getFormat(fractional).format(fractional), unit);
}
public static String formatDataRate(DataSize dataSize, Duration duration, boolean longForm, boolean decimalDataSize)
{
long rate = Math.round(dataSize.toBytes() / duration.getValue(SECONDS));
if (rate == Long.MAX_VALUE) {
rate = 0;
}
String rateString = formatDataSize(DataSize.ofBytes(rate), false, decimalDataSize);
if (longForm) {
if (!rateString.endsWith("B")) {
rateString += "B";
}
rateString += "/s";
}
return rateString;
}
private static DecimalFormat getFormat(double value)
{
DecimalFormat format;
if (value < 10) {
// show up to two decimals to get 3 significant digits
format = new DecimalFormat("#.##");
}
else if (value < 100) {
// show up to one decimal to get 3 significant digits
format = new DecimalFormat("#.#");
}
else {
// show no decimals -- we have enough digits in the integer part
format = new DecimalFormat("#");
}
format.setRoundingMode(RoundingMode.HALF_UP);
return format;
}
public static String pluralize(String word, int count)
{
if (count != 1) {
return word + "s";
}
return word;
}
public static String formatTime(Duration duration)
{
int totalSeconds = Ints.saturatedCast(duration.roundTo(SECONDS));
int minutes = totalSeconds / 60;
int seconds = totalSeconds % 60;
return format("%s:%02d", minutes, seconds);
}
public static String formatFinalTime(Duration duration)
{
long totalMillis = duration.toMillis();
if (totalMillis >= MINUTES.toMillis(1)) {
return formatTime(duration);
}
return format("%.2f", (totalMillis / 1000.0));
}
/**
* Format an indeterminate progress bar: {@code [ <=> ]}
*/
public static String formatProgressBar(int width, int tick)
{
int markerWidth = 3; // must be odd >= 3 (1 for each < and > marker, the rest for "="
int range = width - markerWidth; // "lower" must fall within this range for the marker to fit within the bar
int lower = tick % range;
if (((tick / range) % 2) == 1) { // are we going or coming back?
lower = range - lower;
}
return repeat(" ", lower) +
"<" + repeat("=", markerWidth - 2) + ">" +
repeat(" ", width - (lower + markerWidth));
}
public static String formatProgressBar(int width, int progressPercentage, int runningPercentage)
{
int totalPercentage = 100;
int pending = max(0, totalPercentage - progressPercentage - runningPercentage);
// compute nominal lengths
int completeLength = min(width, ceil(progressPercentage * width, totalPercentage));
int pendingLength = min(width, ceil(pending * width, totalPercentage));
// leave space for at least one ">" as long as runningPercentage is > 0
int minRunningLength = (runningPercentage > 0) ? 1 : 0;
int runningLength = max(min(width, ceil(runningPercentage * width, totalPercentage)), minRunningLength);
// adjust to fix rounding errors
if (((completeLength + runningLength + pendingLength) != width) && (pending > 0)) {
// sacrifice "pending" if we're over the max width
pendingLength = max(0, width - completeLength - runningLength);
}
if ((completeLength + runningLength + pendingLength) != width) {
// then, sacrifice "runningPercentage"
runningLength = max(minRunningLength, width - completeLength - pendingLength);
}
if (((completeLength + runningLength + pendingLength) > width) && (progressPercentage > 0)) {
// finally, sacrifice "progressPercentage" if we're still over the limit
completeLength = max(0, width - runningLength - pendingLength);
}
checkState((completeLength + runningLength + pendingLength) == width,
"Expected completeLength (%s) + runningLength (%s) + pendingLength (%s) == width (%s), was %s for progressPercentage = %s, runningPercentage = %s, totalPercentage = %s",
completeLength, runningLength, pendingLength, width, completeLength + runningLength + pendingLength, progressPercentage, runningPercentage, totalPercentage);
return repeat("=", completeLength) + repeat(">", runningLength) + repeat(" ", pendingLength);
}
/**
* Ceiling of integer division
*/
private static int ceil(int dividend, int divisor)
{
return ((dividend + divisor) - 1) / divisor;
}
static String formatValue(Object o)
{
return formatValue(o, "NULL", 16);
}
static String formatValue(Object o, String nullValue, int bytesPerLine)
{
if (o == null) {
return nullValue;
}
if (o instanceof Map) {
return formatMap((Map, ?>) o);
}
if (o instanceof List) {
return formatList((List>) o);
}
if (o instanceof Row) {
return formatRow(((Row) o));
}
if (o instanceof byte[]) {
return formatHexDump((byte[]) o, bytesPerLine);
}
return o.toString();
}
private static String formatHexDump(byte[] bytes, int bytesPerLine)
{
if (bytesPerLine <= 0) {
return formatHexDump(bytes);
}
// hex pairs: ["61", "62", "63"]
Iterable hexPairs = createHexPairs(bytes);
// hex lines: [["61", "62", "63], [...]]
Iterable> hexLines = partition(hexPairs, bytesPerLine);
// lines: ["61 62 63", ...]
Iterable lines = transform(hexLines, HEX_BYTE_JOINER::join);
// joined: "61 62 63\n..."
return HEX_LINE_JOINER.join(lines);
}
static String formatHexDump(byte[] bytes)
{
return HEX_BYTE_JOINER.join(createHexPairs(bytes));
}
private static Iterable createHexPairs(byte[] bytes)
{
// hex dump: "616263"
String hexDump = base16().lowerCase().encode(bytes);
// hex pairs: ["61", "62", "63"]
return HEX_SPLITTER.split(hexDump);
}
static String formatList(List extends Object> list)
{
return list.stream()
.map(FormatUtils::formatValue)
.collect(joining(", ", "[", "]"));
}
static String formatMap(Map extends Object, ? extends Object> map)
{
return map.entrySet().stream()
.map(entry -> format("%s=%s", formatValue(entry.getKey()), formatValue(entry.getValue())))
.collect(joining(", ", "{", "}"));
}
static String formatRow(Row row)
{
return row.getFields().stream()
.map(field -> {
String formattedValue = formatValue(field.getValue());
if (field.getName().isPresent()) {
return format("%s=%s", formatValue(field.getName().get()), formattedValue);
}
return formattedValue;
})
.collect(joining(", ", "{", "}"));
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy