github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/markup/csv/csv.go (about)

     1  // Copyright 2023 The GitBundle Inc. All rights reserved.
     2  // Copyright 2017 The Gitea Authors. All rights reserved.
     3  // Use of this source code is governed by a MIT-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package markup
     7  
     8  import (
     9  	"bufio"
    10  	"bytes"
    11  	"html"
    12  	"io"
    13  	"regexp"
    14  	"strconv"
    15  
    16  	"github.com/gitbundle/modules/csv"
    17  	"github.com/gitbundle/modules/markup"
    18  	"github.com/gitbundle/modules/setting"
    19  )
    20  
    21  func init() {
    22  	markup.RegisterRenderer(Renderer{})
    23  }
    24  
    25  // Renderer implements markup.Renderer for csv files
    26  type Renderer struct{}
    27  
    28  // Name implements markup.Renderer
    29  func (Renderer) Name() string {
    30  	return "csv"
    31  }
    32  
    33  // Extensions implements markup.Renderer
    34  func (Renderer) Extensions() []string {
    35  	return []string{".csv", ".tsv"}
    36  }
    37  
    38  // SanitizerRules implements markup.Renderer
    39  func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
    40  	return []setting.MarkupSanitizerRule{
    41  		{Element: "table", AllowAttr: "class", Regexp: regexp.MustCompile(`data-table`)},
    42  		{Element: "th", AllowAttr: "class", Regexp: regexp.MustCompile(`line-num`)},
    43  		{Element: "td", AllowAttr: "class", Regexp: regexp.MustCompile(`line-num`)},
    44  	}
    45  }
    46  
    47  func writeField(w io.Writer, element, class, field string) error {
    48  	if _, err := io.WriteString(w, "<"); err != nil {
    49  		return err
    50  	}
    51  	if _, err := io.WriteString(w, element); err != nil {
    52  		return err
    53  	}
    54  	if len(class) > 0 {
    55  		if _, err := io.WriteString(w, " class=\""); err != nil {
    56  			return err
    57  		}
    58  		if _, err := io.WriteString(w, class); err != nil {
    59  			return err
    60  		}
    61  		if _, err := io.WriteString(w, "\""); err != nil {
    62  			return err
    63  		}
    64  	}
    65  	if _, err := io.WriteString(w, ">"); err != nil {
    66  		return err
    67  	}
    68  	if _, err := io.WriteString(w, html.EscapeString(field)); err != nil {
    69  		return err
    70  	}
    71  	if _, err := io.WriteString(w, "</"); err != nil {
    72  		return err
    73  	}
    74  	if _, err := io.WriteString(w, element); err != nil {
    75  		return err
    76  	}
    77  	_, err := io.WriteString(w, ">")
    78  	return err
    79  }
    80  
    81  // Render implements markup.Renderer
    82  func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
    83  	tmpBlock := bufio.NewWriter(output)
    84  
    85  	// FIXME: don't read all to memory
    86  	rawBytes, err := io.ReadAll(input)
    87  	if err != nil {
    88  		return err
    89  	}
    90  
    91  	if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < int64(len(rawBytes)) {
    92  		if _, err := tmpBlock.WriteString("<pre>"); err != nil {
    93  			return err
    94  		}
    95  		if _, err := tmpBlock.WriteString(html.EscapeString(string(rawBytes))); err != nil {
    96  			return err
    97  		}
    98  		_, err = tmpBlock.WriteString("</pre>")
    99  		return err
   100  	}
   101  
   102  	rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, bytes.NewReader(rawBytes))
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	if _, err := tmpBlock.WriteString(`<table class="data-table">`); err != nil {
   108  		return err
   109  	}
   110  	row := 1
   111  	for {
   112  		fields, err := rd.Read()
   113  		if err == io.EOF {
   114  			break
   115  		}
   116  		if err != nil {
   117  			continue
   118  		}
   119  		if _, err := tmpBlock.WriteString("<tr>"); err != nil {
   120  			return err
   121  		}
   122  		element := "td"
   123  		if row == 1 {
   124  			element = "th"
   125  		}
   126  		if err := writeField(tmpBlock, element, "line-num", strconv.Itoa(row)); err != nil {
   127  			return err
   128  		}
   129  		for _, field := range fields {
   130  			if err := writeField(tmpBlock, element, "", field); err != nil {
   131  				return err
   132  			}
   133  		}
   134  		if _, err := tmpBlock.WriteString("</tr>"); err != nil {
   135  			return err
   136  		}
   137  
   138  		row++
   139  	}
   140  	if _, err = tmpBlock.WriteString("</table>"); err != nil {
   141  		return err
   142  	}
   143  	return tmpBlock.Flush()
   144  }