public function renderTable() : string
    {
        if ($this->table->isEmpty()) throw new BuilderException('Cannot render empty table');

        $visibleColumns = $this->table->getVisibleColumns();

        // border for header and footer
        $borderParts = array_map(function ($columnName) {
            $width = $this->table->getColumnWidth($columnName);
            return str_repeat(self::CHAR_LINE_SEPARATOR, ($width + 2));
        }, $visibleColumns->toArray());

        $border = self::CHAR_CORNER_SEPARATOR
                . join(self::CHAR_CORNER_SEPARATOR, $borderParts)
                . self::CHAR_CORNER_SEPARATOR;

        $headerCells = array_map(function ($columnName) {
            return new Cell($columnName, $columnName);
        }, $visibleColumns->toArray());

        $headerRow = new Row();
        $headerRow->addCells(...$headerCells);
        $header = $this->renderRow($headerRow, $visibleColumns);

        $body = '';
        $rows = $this->table->getRows();
        $visibleColumns = $this->table->getVisibleColumns();
        foreach ($rows as $row) {
            $currentLine = $this->renderRow($row, $visibleColumns);
            $body .= $currentLine . PHP_EOL;
        }

        $tableAsString = $border . PHP_EOL . $header . PHP_EOL . $border . PHP_EOL . $body . $border;
        return $tableAsString;
    }