<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"sort"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/urfave/cli"
)

// Metric has a name, type, help and cardinality.
type Metric struct {
	Name        string
	Type        string
	Help        string
	Cardinality int
}

func main() {
	app := cli.NewApp()

	app.Action = ViewAction
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "file, f",
			Usage: "Read metrics from file",
		},
		cli.StringFlag{
			Name:  "sort",
			Usage: "Sort by name, type or help",
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

// ViewAction runs the default cli app.
func ViewAction(c *cli.Context) error {
	url := c.Args().First()
	sortFlag := c.String("sort")
	fileFlag := c.String("file")

	var metrics []Metric
	if fileFlag != "" {
		fileMetrics, err := FileMetrics(fileFlag)
		if err != nil {
			return err
		}
		metrics = fileMetrics
	} else {
		if url == "" {
			return errors.New("Please provide a URL, like http://localhost:8080/metrics")
		}
		httpMetrics, err := HTTPMetrics(url)
		if err != nil {
			return err
		}
		metrics = httpMetrics
	}

	switch sortFlag {
	case "type":
		sort.Slice(metrics, func(i, j int) bool {
			return metrics[i].Type < metrics[j].Type
		})
	case "help":
		sort.Slice(metrics, func(i, j int) bool {
			return metrics[i].Help < metrics[j].Help
		})
	default:
		sort.Slice(metrics, func(i, j int) bool {
			return metrics[i].Name < metrics[j].Name
		})
	}

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', tabwriter.TabIndent)
	fmt.Fprintln(w, "Name\tType\tCardinality\tHelp")
	for _, metric := range metrics {
		if metric.Name != "" {
			fmt.Fprintf(w, "%s\t%s\t%d\t%s\n", metric.Name, metric.Type, metric.Cardinality, metric.Help)
		}
	}
	return w.Flush()

}

// FileMetrics reads a file and returns its metrics.
func FileMetrics(filename string) ([]Metric, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	return parseMetrics(file)
}

// HTTPMetrics makes an HTTP GET request again url and returns its metrics.
func HTTPMetrics(url string) ([]Metric, error) {
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return nil, err
	}<commit_msg>Implement an http server to start a UI<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"sort"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/urfave/cli"
)

// Metric has a name, type, help and cardinality.
type Metric struct {
	Name        string
	Type        string
	Help        string
	Cardinality int
}

func main() {
	app := cli.NewApp()

	app.Action = ViewAction
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "file, f",
			Usage: "Read metrics from file",
		},
		cli.BoolFlag{
			Name:  "http",
			Usage: "Show the overview via a website",
		},
		cli.StringFlag{
			Name:  "sort",
			Usage: "Sort by name, type or help",
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

// ViewAction runs the default cli app.
func ViewAction(c *cli.Context) error {
	url := c.Args().First()
	sortFlag := c.String("sort")
	httpFlag := c.Bool("http")
	fileFlag := c.String("file")

	var metrics []Metric
	if fileFlag != "" {
		fileMetrics, err := FileMetrics(fileFlag)
		if err != nil {
			return err
		}
		metrics = fileMetrics
	} else {
		if url == "" {
			return errors.New("Please provide a URL, like http://localhost:8080/metrics")
		}
		httpMetrics, err := HTTPMetrics(url)
		if err != nil {
			return err
		}
		metrics = httpMetrics
	}

	switch sortFlag {
	case "type":
		sort.Slice(metrics, func(i, j int) bool {
			return metrics[i].Type < metrics[j].Type
		})
	case "help":
		sort.Slice(metrics, func(i, j int) bool {
			return metrics[i].Help < metrics[j].Help
		})
	default:
		sort.Slice(metrics, func(i, j int) bool {
			return metrics[i].Name < metrics[j].Name
		})
	}

	if httpFlag {
		return printHttp(metrics)
	}

	return printCli(metrics)
}

func printHttp(metrics []Metric) error {
	html := `
	<html>
		<head>
			<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
			<title>prom-metric-viewer</title>
		</head>
		<body>
			<table class="table table-hover">
				<tbody>
				{{range .}}
				<tr>
				<td>{{.Name}}</td>
				<td>{{.Type}}</td>
				<td>{{.Cardinality}}</td>
				<td>{{.Help}}</td>
				</tr>
				{{end}}
				</tbody>
			</table>
		</body>
	</html>
	`
	tem := template.New("")
	tem.Parse(html)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		tem.Execute(w, metrics)
	})

	return http.ListenAndServe(":8080", nil)
}

func printCli(metrics []Metric) error {
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', tabwriter.TabIndent)
	fmt.Fprintln(w, "Name\tType\tCardinality\tHelp")
	for _, metric := range metrics {
		if metric.Name != "" {
			fmt.Fprintf(w, "%s\t%s\t%d\t%s\n", metric.Name, metric.Type, metric.Cardinality, metric.Help)
		}
	}
	return w.Flush()
}

// FileMetrics reads a file and returns its metrics.
func FileMetrics(filename string) ([]Metric, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	return parseMetrics(file)
}

// HTTPMetrics makes an HTTP GET request again url and returns its metrics.
func HTTPMetrics(url string) ([]Metric, error) {
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return nil, err
	}
{'fraction_non_alphanumeric': 0.1619692532942899, 'fraction_numerical': 0.0038433382137628112, 'mean_word_length': 3.2266047950502705, 'pattern_counts': {'":': 5, '<': 32, '<?xml version=': 0, '>': 26, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4051775', 'n_tokens_mistral': 2223, 'n_tokens_neox': 2046, 'n_words': 522}