253 lines
6.1 KiB
Go
253 lines
6.1 KiB
Go
/*
|
|
* Wiki - A wiki with editor
|
|
* Copyright (c) 2021 Peter Stuifzand
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"math/rand"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"p83.nl/go/wiki/link"
|
|
)
|
|
|
|
var (
|
|
MetaKV = regexp.MustCompile(`(\w+)::\s+(.*)`)
|
|
niceDateParseRE = regexp.MustCompile(`^(\d{1,2})_([a-z]+)_(\d{4})$`)
|
|
ParseFailed = errors.New("parse failed")
|
|
Months = []string{
|
|
"",
|
|
"januari",
|
|
"februari",
|
|
"maart",
|
|
"april",
|
|
"mei",
|
|
"juni",
|
|
"juli",
|
|
"augustus",
|
|
"september",
|
|
"oktober",
|
|
"november",
|
|
"december",
|
|
}
|
|
)
|
|
|
|
type ParsedLink struct {
|
|
ID string `json:"ID,omitempty"`
|
|
Name string `json:"title,omitempty"`
|
|
PageName string `json:"name,omitempty"`
|
|
Line string `json:"line,omitempty"`
|
|
Href string `json:"href,omitempty"`
|
|
}
|
|
|
|
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
|
func RandStringBytes(n int) string {
|
|
b := make([]byte, n)
|
|
for i := range b {
|
|
b[i] = letterBytes[rand.Intn(len(letterBytes))]
|
|
}
|
|
return string(b)
|
|
}
|
|
|
|
type DateLink struct {
|
|
Link string
|
|
Date time.Time
|
|
}
|
|
|
|
func ParseDates(content string) ([]time.Time, error) {
|
|
links := link.FindAllLinks(content)
|
|
var result []time.Time
|
|
for _, linkName := range links {
|
|
date, err := ParseDatePageName(linkName)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
result = append(result, date)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func ParseLinks(blockId string, content string) ([]ParsedLink, error) {
|
|
hrefRE := regexp.MustCompile(`(#?\[\[\s*([^\]]+)\s*\]\])`)
|
|
// keywordsRE := regexp.MustCompile(`(\w+)::`)
|
|
|
|
scanner := bufio.NewScanner(strings.NewReader(content))
|
|
scanner.Split(bufio.ScanLines)
|
|
|
|
var result []ParsedLink
|
|
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
|
|
// keywords := keywordsRE.FindAllStringSubmatch(line, -1)
|
|
// for _, matches := range keywords {
|
|
// link := matches[1]
|
|
// l := cleanNameURL(link)
|
|
// result = append(result, ParsedLink{blockId, link, l, line, ""})
|
|
// }
|
|
|
|
links := hrefRE.FindAllStringSubmatch(line, -1)
|
|
|
|
for _, matches := range links {
|
|
link := matches[0]
|
|
|
|
if link[0] == '#' {
|
|
link = strings.TrimPrefix(link, "#[[")
|
|
} else {
|
|
link = strings.TrimPrefix(link, "[[")
|
|
}
|
|
|
|
link = strings.TrimSuffix(link, "]]")
|
|
link = strings.TrimSpace(link)
|
|
l := cleanNameURL(link)
|
|
result = append(result, ParsedLink{blockId, link, l, line, ""})
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func ParseTags(content string) ([]string, error) {
|
|
linkRE := regexp.MustCompile(`(#\[\[\s*([^\]]+)\s*\]\])`)
|
|
tagRE := regexp.MustCompile(`#([^ ]+)`)
|
|
|
|
scanner := bufio.NewScanner(strings.NewReader(content))
|
|
scanner.Split(bufio.ScanLines)
|
|
|
|
var result []string
|
|
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
links := linkRE.FindAllStringSubmatch(line, -1)
|
|
for _, matches := range links {
|
|
linkText := matches[0]
|
|
linkText = strings.TrimPrefix(linkText, "#[[")
|
|
linkText = strings.TrimSuffix(linkText, "]]")
|
|
linkText = strings.TrimSpace(linkText)
|
|
result = append(result, linkText)
|
|
}
|
|
for _, matches := range tagRE.FindAllStringSubmatch(line, -1) {
|
|
result = append(result, matches[1])
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func cleanNameURL(name string) string {
|
|
return strings.Replace(name, " ", "_", -1)
|
|
}
|
|
|
|
func cleanTitle(name string) string {
|
|
return strings.Replace(name, "_", " ", -1)
|
|
}
|
|
|
|
type stopwatch struct {
|
|
start time.Time
|
|
lastLap time.Time
|
|
label string
|
|
}
|
|
|
|
func (sw *stopwatch) Start(label string) {
|
|
sw.start = time.Now()
|
|
sw.lastLap = time.Now()
|
|
sw.label = label
|
|
}
|
|
|
|
func (sw *stopwatch) Lap(label string) {
|
|
now := time.Now()
|
|
d := now.Sub(sw.lastLap)
|
|
log.Printf("%-20s: %s\n", label, d.String())
|
|
sw.lastLap = now
|
|
}
|
|
|
|
func (sw *stopwatch) Stop() {
|
|
endTime := time.Now()
|
|
d := endTime.Sub(sw.start)
|
|
log.Printf("%-20s: %s\n", sw.label, d.String())
|
|
}
|
|
|
|
func todayPage() string {
|
|
now := time.Now()
|
|
return formatDatePageName(now)
|
|
}
|
|
|
|
func formatDateTitle(date time.Time) string {
|
|
return fmt.Sprintf("%d %s %d", date.Day(), Months[date.Month()], date.Year())
|
|
}
|
|
func formatDatePageName(date time.Time) string {
|
|
return fmt.Sprintf("%d_%s_%d", date.Day(), Months[date.Month()], date.Year())
|
|
}
|
|
|
|
func PageTitle(pageText string) (string, error) {
|
|
var listItems []struct {
|
|
Indented int
|
|
Text string
|
|
}
|
|
err := json.NewDecoder(strings.NewReader(pageText)).Decode(&listItems)
|
|
if err != nil {
|
|
return "", fmt.Errorf("while decoding page text: %w", err)
|
|
}
|
|
|
|
for _, li := range listItems {
|
|
if matches := MetaKV.FindStringSubmatch(li.Text); matches != nil {
|
|
if matches[1] == "Title" {
|
|
return matches[2], nil
|
|
}
|
|
}
|
|
}
|
|
return "", fmt.Errorf("no meta title found in page text")
|
|
}
|
|
|
|
func parseMonth(month string) (time.Month, error) {
|
|
for i, m := range Months {
|
|
if m == month {
|
|
return time.Month(i), nil
|
|
}
|
|
}
|
|
return time.Month(0), fmt.Errorf("parseMonth: %q is not a recognized month", month)
|
|
}
|
|
|
|
func ParseDatePageName(name string) (time.Time, error) {
|
|
if matches := niceDateParseRE.FindStringSubmatch(strings.Replace(name, " ", "_", -1)); matches != nil {
|
|
day, err := strconv.Atoi(matches[1])
|
|
if err != nil {
|
|
return time.Time{}, fmt.Errorf("%q: %s: %w", name, err, ParseFailed)
|
|
}
|
|
month, err := parseMonth(matches[2])
|
|
if err != nil {
|
|
return time.Time{}, fmt.Errorf("%q: %s: %w", name, err, ParseFailed)
|
|
}
|
|
year, err := strconv.Atoi(matches[3])
|
|
if err != nil {
|
|
return time.Time{}, fmt.Errorf("%q: %s: %w", name, err, ParseFailed)
|
|
}
|
|
return time.Date(year, month, day, 0, 0, 0, 0, time.UTC), nil
|
|
}
|
|
return time.Time{}, fmt.Errorf("%q: invalid syntax: %w", name, ParseFailed)
|
|
}
|