Skip to content

Implement Text to ASCII Art Generator with Standard and Block Fonts#139

Open
MudasirStanikzay wants to merge 1 commit intomainfrom
feat/ascii-art-generator
Open

Implement Text to ASCII Art Generator with Standard and Block Fonts#139
MudasirStanikzay wants to merge 1 commit intomainfrom
feat/ascii-art-generator

Conversation

@MudasirStanikzay
Copy link
Copy Markdown

@MudasirStanikzay MudasirStanikzay commented Sep 1, 2025

This PR implements a utility for converting plain text into ASCII art.
Supports Standard and Block font styles.

Summary by CodeRabbit

  • New Features
    • Added text-to-ASCII art rendering with selectable font styles (Standard and Block).
    • Provides clear validation errors for empty or null input, overly long text (50+ characters), and unsupported characters.
  • Tests
    • Introduced comprehensive tests covering successful rendering and validation scenarios (null, empty, excessive length).

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 1, 2025

Walkthrough

Adds a new ASCII art generation feature under org.fungover.breeze.ascii2: an AsciiFont interface, two font implementations (StandardFont, BlockFont), an AsciiArtGenerator orchestrator with input validation and length check, and unit tests validating rendering and error conditions.

Changes

Cohort / File(s) Summary
ASCII Art Core
src/main/java/.../ascii2/AsciiFont.java, src/main/java/.../ascii2/AsciiArtGenerator.java
Introduces AsciiFont interface (render, getName) and AsciiArtGenerator that validates input (null/blank, max length 50) and delegates to font.render.
Fonts
src/main/java/.../ascii2/StandardFont.java, src/main/java/.../ascii2/BlockFont.java
Adds two 3-line ASCII font implementations with internal glyph maps, input validation, unsupported-character checks, and getName returning "Standard" and "Block".
Tests
src/test/java/.../ascii2/AsciiArtGeneratorTest.java
Adds unit tests for generator: successful renders with Standard and Block fonts; exceptions for null, empty, and overlength input.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Client
  participant G as AsciiArtGenerator
  participant F as AsciiFont<br/>(StandardFont/BlockFont)

  C->>G: generate(text)
  alt text null/blank
    G-->>C: throw IllegalArgumentException("Text cannot be null or empty")
  else text length > 50
    G-->>C: throw IllegalArgumentException("Text too long. Max 50 characters.")
  else valid
    G->>F: render(text)
    alt unsupported character in font
      F-->>G: throw IllegalArgumentException("Unsupported character: X")
      G-->>C: propagate exception
    else supported
      F-->>G: asciiArt
      G-->>C: asciiArt
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • Biggienation
  • EvgeniiaGranina

Poem

I nibble on code like clover at dawn,
Stitching letters in blocks, artfully drawn.
HELLO hops by in a three-line cheer,
Standard or Block—both crisp and clear.
Bugs beware: my whiskers twitch—
One hop, two hops, perfect glyphs, no glitch! 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/ascii-art-generator

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (12)
src/main/java/org/fungover/breeze/ascii2/AsciiFont.java (1)

3-6: Document the contract (rows, newlines, unsupported chars) in the interface.

Clarify that implementations should return exactly N rows separated by '\n' (and whether a trailing '\n' is expected), and how unsupported characters are handled. This keeps fonts consistent and testing simpler.

Apply Javadoc (non-breaking):

 public interface AsciiFont {
-    String render(String text);
-    String getName();
+    /**
+     * Renders the given text as multi-line ASCII art.
+     * Implementations should:
+     * - Use a fixed row height per font.
+     * - Return rows separated by '\n'. Trailing '\n' is allowed.
+     * - Throw IllegalArgumentException for unsupported characters.
+     */
+    String render(String text);
+    /** Human-friendly font name (e.g., "Standard", "Block"). */
+    String getName();
 }
src/test/java/org/fungover/breeze/ascii2/AsciiArtGeneratorTest.java (3)

9-13: These assertions are very loose; add structure checks.

Add a test that verifies the output has exactly 3 rows (after stripTrailing) to catch formatting regressions.

     void testStandardFontHello() {
         AsciiArtGenerator gen = new AsciiArtGenerator(new StandardFont());
         String output = gen.generate("HELLO");
         assertTrue(output.contains("H  H"));
+        assertEquals(3, output.stripTrailing().split("\n").length);
     }

34-39: Add boundary tests for max length.

Verify exactly 50 chars passes and 51 fails to lock the contract.

     void testTooLongInputThrowsException() {
         AsciiArtGenerator gen = new AsciiArtGenerator(new StandardFont());
         String longText = "A".repeat(60);
         assertThrows(IllegalArgumentException.class, () -> gen.generate(longText));
     }
+
+    @Test
+    void testMaxLengthAccepted() {
+        AsciiArtGenerator gen = new AsciiArtGenerator(new StandardFont());
+        String exact = "A".repeat(50);
+        String out = gen.generate(exact);
+        assertFalse(out.isBlank());
+    }
+
+    @Test
+    void testOverMaxLengthRejected() {
+        AsciiArtGenerator gen = new AsciiArtGenerator(new StandardFont());
+        String over = "A".repeat(51);
+        assertThrows(IllegalArgumentException.class, () -> gen.generate(over));
+    }

15-20: Decide on space handling and add tests accordingly.

Currently, fonts do not map ' ' and will throw. If you want “HELLO WORLD” to work, add a space glyph and test it; otherwise, add a test asserting it throws to make the behavior explicit.

src/main/java/org/fungover/breeze/ascii2/AsciiArtGenerator.java (1)

11-15: LGTM; consider extracting constants.

Minor: Extract the 50 limit and error messages to named constants for reuse and clarity.

 public class AsciiArtGenerator {
 
     private final AsciiFont font;
+    private static final int MAX_LENGTH = 50;
+    private static final String ERR_EMPTY = "Text cannot be null or empty";
+    private static final String ERR_TOO_LONG = "Text too long. Max 50 characters.";
 
     public AsciiArtGenerator(AsciiFont font) {
         this.font = font;
     }
 
     public String generate(String text) {
-        if (text == null || text.isBlank()) throw new IllegalArgumentException("Text cannot be null or empty");
-        if (text.length() > 50) throw new IllegalArgumentException("Text too long. Max 50 characters.");
+        if (text == null || text.isBlank()) throw new IllegalArgumentException(ERR_EMPTY);
+        if (text.length() > MAX_LENGTH) throw new IllegalArgumentException(ERR_TOO_LONG);
         return font.render(text);
     }
 }
src/main/java/org/fungover/breeze/ascii2/BlockFont.java (3)

14-35: Support spaces for usability.

Map ' ' to a blank glyph so phrases work (e.g., “HELLO WORLD”) instead of throwing.

     private void loadFont() {
+        // space (width matches other glyphs)
+        fontMap.put(' ', new String[]{
+                "      ",
+                "      ",
+                "      "
+        });
         fontMap.put('H', new String[]{
                 "██  ██",
                 "██████",
                 "██  ██"
         });

38-53: Minor perf/readability: avoid toCharArray() per row and cache glyph lookups.

This reduces allocations and map hits in the inner loop.

     @Override
     public String render(String text) {
         if (text == null || text.isBlank()) throw new IllegalArgumentException("Text cannot be null or empty");
         text = text.toUpperCase();
-        StringBuilder sb = new StringBuilder();
-        int height = 3;
-        for (int row = 0; row < height; row++) {
-            for (char c : text.toCharArray()) {
-                if (!fontMap.containsKey(c)) {
-                    throw new IllegalArgumentException("Unsupported character: " + c);
-                }
-                sb.append(fontMap.get(c)[row]).append("  ");
-            }
-            sb.append("\n");
-        }
+        final int height = 3;
+        final char[] chars = text.toCharArray();
+        StringBuilder sb = new StringBuilder(text.length() * (height * 8)); // rough capacity
+        for (int row = 0; row < height; row++) {
+            for (char c : chars) {
+                String[] glyph = fontMap.get(c);
+                if (glyph == null) throw new IllegalArgumentException("Unsupported character: " + c);
+                sb.append(glyph[row]).append("  ");
+            }
+            sb.append('\n');
+        }
         return sb.toString();
     }

6-12: Immutable font map (optional).

After load, wrap with Collections.unmodifiableMap to prevent accidental mutation.

src/main/java/org/fungover/breeze/ascii2/StandardFont.java (4)

15-16: Use English for inline comments.

Keep code comments consistent with the codebase language.

-        // Förenklad version, fler kan läggas till
+        // Simplified version; more glyphs can be added

14-46: Support spaces for phrases.

Add a space glyph so multi-word inputs don’t error.

     private void loadFont() {
+        fontMap.put(' ', new String[]{
+                "    ",
+                "    ",
+                "    "
+        });
         fontMap.put('A', new String[]{
                 "  A  ",
                 " A A ",
                 "AAAAA"
         });

48-64: Minor perf/readability: cache chars and glyphs inside loops.

Same micro-optimization as in BlockFont.

     @Override
     public String render(String text) {
         if (text == null || text.isBlank()) throw new IllegalArgumentException("Text cannot be null or empty");
         text = text.toUpperCase();
-        StringBuilder sb = new StringBuilder();
-        int height = 3;
-        for (int row = 0; row < height; row++) {
-            for (char c : text.toCharArray()) {
-                if (!fontMap.containsKey(c)) {
-                    throw new IllegalArgumentException("Unsupported character: " + c);
-                }
-                sb.append(fontMap.get(c)[row]).append("  ");
-            }
-            sb.append("\n");
-        }
+        final int height = 3;
+        final char[] chars = text.toCharArray();
+        StringBuilder sb = new StringBuilder(text.length() * (height * 6)); // rough capacity
+        for (int row = 0; row < height; row++) {
+            for (char c : chars) {
+                String[] glyph = fontMap.get(c);
+                if (glyph == null) throw new IllegalArgumentException("Unsupported character: " + c);
+                sb.append(glyph[row]).append("  ");
+            }
+            sb.append('\n');
+        }
         return sb.toString();
     }

6-12: Consider sharing common rendering logic.

Both fonts duplicate rendering/validation. An abstract base (e.g., AbstractAsciiFont with height, render loop, and a protected glyph lookup) would reduce duplication and centralize behavior.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between beeafc7 and 45fe567.

📒 Files selected for processing (5)
  • src/main/java/org/fungover/breeze/ascii2/AsciiArtGenerator.java (1 hunks)
  • src/main/java/org/fungover/breeze/ascii2/AsciiFont.java (1 hunks)
  • src/main/java/org/fungover/breeze/ascii2/BlockFont.java (1 hunks)
  • src/main/java/org/fungover/breeze/ascii2/StandardFont.java (1 hunks)
  • src/test/java/org/fungover/breeze/ascii2/AsciiArtGeneratorTest.java (1 hunks)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simple, clear responsibility: delegates rendering to AsciiFont.

Good input validation (null, blank, max length).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean interface with only necessary methods (render, getName).

Encourages different font implementations.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good encapsulation with fontMap and loadFont().

Provides clear error when encountering unsupported characters.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar consistent structure as BlockFont.

Easy to extend with more characters.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests cover valid rendering, invalid inputs (null, empty, too long).

Checks that different fonts produce expected output.

Clear use of JUnit assertions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants