package html
import (
"os"
"path"
"serveg/internal/http"
"serveg/internal/log"
sh "serveg/internal/string_helper"
)
type listingRow struct {
name string // html-escaped
url string // url-encoded
mtime string // formatted with DateTimeTZ
size string // formatted with HumanBytes
isDir bool
}
func (row *listingRow) genHtml(sb *sh.StringBuilder) {
sb.AddS(`
`)
sb.AddS(`| ').AddS(row.name).AddS(` | `)
sb.AddS(``).AddS(row.size).AddS(` | `)
sb.AddS(``).AddS(row.mtime).AddS(` | `)
sb.AddS(`
`)
}
func GenDirListingHtml(logger *log.LoggerContext, dirPath, reqUrl, pageTitle string) ([]byte, error) {
dirEntries, err := os.ReadDir(dirPath)
if err != nil {
return nil, err
}
var dirs, files []listingRow
var estimatedLen = 460 // html for page without entries
for _, e := range dirEntries {
einf, err := e.Info()
if err != nil {
return nil, err
}
row := listingRow{
name: htmlEscape(e.Name()),
url: http.PercentEcodeUrl(path.Join(reqUrl, e.Name())),
mtime: einf.ModTime().Format(DateTimeTZ),
isDir: e.IsDir(),
}
if row.isDir {
row.size = "DIR"
dirs = append(dirs, row)
} else {
row.size = sh.HumanBytes(einf.Size())
files = append(files, row)
}
estimatedLen += len(row.name) + len(row.url) + len(row.mtime) + len(row.size)
estimatedLen += 100 // html for row
}
sb := sh.StringBuilder{Buf: make([]byte, 0, estimatedLen)}
sb.AddS(`
`)
sb.AddS(htmlEscape(pageTitle + " " + reqUrl))
sb.AddS(`
`)
sb.AddS("").AddS(htmlEscape(reqUrl)).AddS("
")
sb.AddS(``)
sb.AddS(`| ../ |
`)
for i := 0; i < len(dirs); i++ {
dirs[i].genHtml(&sb)
}
for i := 0; i < len(files); i++ {
files[i].genHtml(&sb)
}
sb.AddS(`
`)
realLen := sb.Len()
if estimatedLen < realLen {
logger.Warn("genDirListingHtml(): size prediction failed. estimatedLen: ",
estimatedLen, " realLen: ", realLen)
}
return sb.Buf, nil
}