diff --git a/eternalcode-commons-shared/src/main/java/com/eternalcode/commons/progressbar/ProgressBar.java b/eternalcode-commons-shared/src/main/java/com/eternalcode/commons/progressbar/ProgressBar.java new file mode 100644 index 0000000..9537967 --- /dev/null +++ b/eternalcode-commons-shared/src/main/java/com/eternalcode/commons/progressbar/ProgressBar.java @@ -0,0 +1,137 @@ +package com.eternalcode.commons.progressbar; + +public class ProgressBar { + + private final String filledToken; + + private final String emptyToken; + + private final boolean showBrackets; + private final String leftBracket; + private final String rightBracket; + private final String bracketColor; + + private final int length; + + private ProgressBar(Builder builder) { + this.leftBracket = builder.leftBracket; + this.rightBracket = builder.rightBracket; + this.bracketColor = builder.bracketColor; + this.length = builder.length; + this.showBrackets = builder.showBrackets; + + this.filledToken = builder.filledColor + builder.filledChar; + this.emptyToken = builder.emptyColor + builder.emptyChar; + } + + public String render(double progress) { + double clampedProgress = Math.max(0, Math.min(1, progress)); + int filled = (int) (this.length * clampedProgress); + + StringBuilder bar = new StringBuilder(); + + if (this.showBrackets) { + bar.append(this.bracketColor).append(this.leftBracket); + } + + bar.append(this.filledToken.repeat(filled)); + bar.append(this.emptyToken.repeat(this.length - filled)); + + if (this.showBrackets) { + bar.append(this.bracketColor).append(this.rightBracket); + } + + return bar.toString(); + } + + public String render(int current, int max) { + return this.render(current, (long) max); + } + + public String render(long current, long max) { + if (max <= 0) { + return this.render(1.0); + } + double progress = (double) current / max; + return this.render(progress); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String filledChar = "█"; + private String emptyChar = "░"; + private String filledColor = ""; + private String emptyColor = ""; + private String leftBracket = "["; + private String rightBracket = "]"; + private String bracketColor = ""; + private int length = 10; + private boolean showBrackets = true; + + public Builder filledChar(String filledChar) { + this.filledChar = filledChar; + return this; + } + + public Builder emptyChar(String emptyChar) { + this.emptyChar = emptyChar; + return this; + } + + public Builder filledColor(String filledColor) { + this.filledColor = filledColor; + return this; + } + + public Builder emptyColor(String emptyColor) { + this.emptyColor = emptyColor; + return this; + } + + public Builder leftBracket(String leftBracket) { + this.leftBracket = leftBracket; + return this; + } + + public Builder rightBracket(String rightBracket) { + this.rightBracket = rightBracket; + return this; + } + + public Builder brackets(String left, String right) { + this.leftBracket = left; + this.rightBracket = right; + return this; + } + + public Builder bracketColor(String bracketColor) { + this.bracketColor = bracketColor; + return this; + } + + public Builder length(int length) { + if (length <= 0) { + throw new IllegalArgumentException("Length must be positive"); + } + this.length = length; + return this; + } + + public Builder showBrackets(boolean showBrackets) { + this.showBrackets = showBrackets; + return this; + } + + public Builder hideBrackets() { + this.showBrackets = false; + return this; + } + + public ProgressBar build() { + return new ProgressBar(this); + } + } +} diff --git a/eternalcode-commons-shared/test/com/eternalcode/commons/progressbar/ProgressBarTest.java b/eternalcode-commons-shared/test/com/eternalcode/commons/progressbar/ProgressBarTest.java new file mode 100644 index 0000000..09657fc --- /dev/null +++ b/eternalcode-commons-shared/test/com/eternalcode/commons/progressbar/ProgressBarTest.java @@ -0,0 +1,108 @@ +package com.eternalcode.commons.progressbar; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ProgressBarTest { + + @Test + void testRenderFullProgress() { + ProgressBar bar = ProgressBar.builder() + .length(5) + .build(); + String rendered = bar.render(1.0); + + assertEquals("[█████]", rendered); + } + + @Test + void testRenderEmptyProgress() { + ProgressBar bar = ProgressBar.builder() + .length(5) + .build(); + String rendered = bar.render(0.0); + + assertEquals("[░░░░░]", rendered); + } + + @Test + void testRenderHalfProgress() { + ProgressBar bar = ProgressBar.builder() + .length(4) + .build(); + String rendered = bar.render(0.5); + + assertEquals("[██░░]", rendered); + } + + @Test + void testRenderIntOverMax() { + ProgressBar bar = ProgressBar.builder() + .length(3) + .build(); + String rendered = bar.render(5, 3); + + assertEquals("[███]", rendered); + } + + @Test + void testRenderIntWithZeroMax() { + ProgressBar bar = ProgressBar.builder() + .length(3) + .build(); + String rendered = bar.render(0, 0); + + assertEquals("[███]", rendered); + } + + @Test + void testHideBrackets() { + ProgressBar bar = ProgressBar.builder() + .length(3) + .hideBrackets() + .build(); + String rendered = bar.render(1.0); + + assertEquals("███", rendered); + } + + @Test + void testCustomCharacters() { + ProgressBar bar = ProgressBar.builder() + .length(4) + .filledChar("#") + .emptyChar("-") + .brackets("{", "}") + .build(); + + String rendered = bar.render(0.5); + assertEquals("{##--}", rendered); + } + + @Test + void testNegativeProgressClampedToZero() { + ProgressBar bar = ProgressBar.builder() + .length(3) + .build(); + String rendered = bar.render(-1.0); + + assertEquals("[░░░]", rendered); + } + + @Test + void testProgressGreaterThanOneClampedToOne() { + ProgressBar bar = ProgressBar.builder() + .length(3) + .build(); + String rendered = bar.render(2.0); + + assertEquals("[███]", rendered); + } + + @Test + void testInvalidLengthThrowsException() { + assertThrows(IllegalArgumentException.class, () -> ProgressBar.builder().length(0)); + assertThrows(IllegalArgumentException.class, () -> ProgressBar.builder().length(-5)); + } +}