Fork Notesium source and restructure into Go package layout
Initial fork of github.com/alonswartz/notesium into librenotes: - Source moved to internal/notesium/ (package notesium) - Thin entry point at cmd/librenotes/main.go - Module renamed to git.librete.ch/public/librenotes - main() exposed as notesium.Run() - LICENSE preserved (MIT), NOTICE added with attribution - Web assets and completion.bash co-located with embedding code to satisfy go:embed path constraints Closes #3, #34, #35. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,605 @@
|
||||
package notesium
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var buildtime = "unset"
|
||||
var gitversion = "unset"
|
||||
|
||||
//go:embed completion.bash web/app
|
||||
var embedfs embed.FS
|
||||
var embedfsWebRoot = "web/app"
|
||||
|
||||
func Run() {
|
||||
cmd, err := parseOptions(os.Args[1:])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
switch cmd.Name {
|
||||
case "help":
|
||||
fmt.Print(usage)
|
||||
os.Exit(1)
|
||||
case "version":
|
||||
notesiumVersion(cmd.Options.(versionOptions), os.Stdout)
|
||||
return
|
||||
}
|
||||
|
||||
notesiumDir, err := getNotesiumDir()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
switch cmd.Name {
|
||||
case "home":
|
||||
fmt.Println(notesiumDir)
|
||||
case "new":
|
||||
notesiumNew(notesiumDir, cmd.Options.(newOptions), os.Stdout)
|
||||
case "list":
|
||||
notesiumList(notesiumDir, cmd.Options.(listOptions), os.Stdout)
|
||||
case "links":
|
||||
notesiumLinks(notesiumDir, cmd.Options.(linksOptions), os.Stdout)
|
||||
case "lines":
|
||||
notesiumLines(notesiumDir, cmd.Options.(linesOptions), os.Stdout)
|
||||
case "stats":
|
||||
notesiumStats(notesiumDir, cmd.Options.(statsOptions), os.Stdout)
|
||||
case "finder":
|
||||
notesiumFinder(notesiumDir, cmd.Options.(finderOptions))
|
||||
case "cat":
|
||||
notesiumCat(notesiumDir, cmd.Options.(catOptions))
|
||||
case "web":
|
||||
notesiumWeb(notesiumDir, cmd.Options.(webOptions))
|
||||
case "extract":
|
||||
notesiumExtract(cmd.Options.(extractOptions))
|
||||
}
|
||||
}
|
||||
|
||||
func notesiumNew(dir string, opts newOptions, w io.Writer) {
|
||||
ctime := time.Now()
|
||||
if opts.ctime != "" {
|
||||
var err error
|
||||
ctime, err = time.ParseInLocation("2006-01-02T15:04:05", opts.ctime, time.Local)
|
||||
if err != nil {
|
||||
log.Fatalf("invalid ctime format: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
epochInt := ctime.Unix()
|
||||
epochHex := fmt.Sprintf("%x", epochInt)
|
||||
filename := fmt.Sprintf("%s.md", epochHex)
|
||||
path := filepath.Join(dir, filename)
|
||||
|
||||
_, err := os.Stat(path)
|
||||
fileExists := !os.IsNotExist(err)
|
||||
|
||||
if opts.verbose {
|
||||
fmt.Fprintf(w, "path:%s\n", path)
|
||||
fmt.Fprintf(w, "filename:%s\n", filename)
|
||||
fmt.Fprintf(w, "epoch:%d\n", epochInt)
|
||||
fmt.Fprintf(w, "ctime:%s\n", ctime.Format("2006-01-02T15:04:05-07:00"))
|
||||
fmt.Fprintf(w, "exists:%t\n", fileExists)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\n", path)
|
||||
}
|
||||
}
|
||||
|
||||
func notesiumList(dir string, opts listOptions, w io.Writer) {
|
||||
populateCache(dir)
|
||||
notes := getSortedNotes(opts.sortBy)
|
||||
|
||||
switch opts.limit {
|
||||
case "labels":
|
||||
for _, note := range notes {
|
||||
if note.IsLabel {
|
||||
fmt.Fprintf(w, "%s:1: %s\n", note.Filename, note.Title)
|
||||
}
|
||||
}
|
||||
return
|
||||
case "orphans":
|
||||
for _, note := range notes {
|
||||
if len(note.OutgoingLinks) == 0 && len(note.IncomingLinks) == 0 {
|
||||
fmt.Fprintf(w, "%s:1: %s\n", note.Filename, note.Title)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch opts.prefix {
|
||||
case "label":
|
||||
var notesWithoutLabelLinks []*Note
|
||||
var outputLines []string
|
||||
for _, note := range notes {
|
||||
labelLinked := false
|
||||
for _, link := range note.OutgoingLinks {
|
||||
if linkNote, exists := noteCache[link.Filename]; exists && linkNote.IsLabel {
|
||||
line := fmt.Sprintf("%s:1: %s%s%s %s", note.Filename, opts.color.Code, linkNote.Title, opts.color.Reset, note.Title)
|
||||
if opts.sortBy == "alpha" {
|
||||
outputLines = append(outputLines, line)
|
||||
} else {
|
||||
fmt.Fprintln(w, line)
|
||||
}
|
||||
labelLinked = true
|
||||
}
|
||||
}
|
||||
if !labelLinked {
|
||||
notesWithoutLabelLinks = append(notesWithoutLabelLinks, note)
|
||||
}
|
||||
}
|
||||
if opts.sortBy == "alpha" {
|
||||
sortLinesByField(outputLines, ": ", 1)
|
||||
for _, line := range outputLines {
|
||||
fmt.Fprintln(w, line)
|
||||
}
|
||||
}
|
||||
for _, note := range notesWithoutLabelLinks {
|
||||
fmt.Fprintf(w, "%s:1: %s\n", note.Filename, note.Title)
|
||||
}
|
||||
return
|
||||
case "ctime":
|
||||
for _, note := range notes {
|
||||
dateStamp := getDateStamp(note.Ctime, opts.dateFormat)
|
||||
fmt.Fprintf(w, "%s:1: %s%s%s %s\n", note.Filename, opts.color.Code, dateStamp, opts.color.Reset, note.Title)
|
||||
}
|
||||
return
|
||||
case "mtime":
|
||||
for _, note := range notes {
|
||||
dateStamp := getDateStamp(note.Mtime, opts.dateFormat)
|
||||
fmt.Fprintf(w, "%s:1: %s%s%s %s\n", note.Filename, opts.color.Code, dateStamp, opts.color.Reset, note.Title)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
for _, note := range notes {
|
||||
fmt.Fprintf(w, "%s:1: %s\n", note.Filename, note.Title)
|
||||
}
|
||||
}
|
||||
|
||||
func notesiumLinks(dir string, opts linksOptions, w io.Writer) {
|
||||
populateCache(dir)
|
||||
|
||||
if opts.filename != "" {
|
||||
note, exists := noteCache[opts.filename]
|
||||
if !exists {
|
||||
log.Printf("filename does not exist: %s\n", opts.filename)
|
||||
return
|
||||
}
|
||||
switch opts.limit {
|
||||
case "outgoing":
|
||||
for _, link := range note.OutgoingLinks {
|
||||
linkNote, exists := noteCache[link.Filename]
|
||||
if exists {
|
||||
fmt.Fprintf(w, "%s:1: %s\n", linkNote.Filename, linkNote.Title)
|
||||
}
|
||||
}
|
||||
return
|
||||
case "incoming":
|
||||
for _, link := range note.IncomingLinks {
|
||||
linkNote, exists := noteCache[link.Filename]
|
||||
if exists {
|
||||
fmt.Fprintf(w, "%s:%d: %s\n", linkNote.Filename, link.LineNumber, linkNote.Title)
|
||||
}
|
||||
}
|
||||
return
|
||||
default:
|
||||
prefix := fmt.Sprintf("%soutgoing%s", opts.color.Code, opts.color.Reset)
|
||||
for _, link := range note.OutgoingLinks {
|
||||
linkNote, exists := noteCache[link.Filename]
|
||||
if exists {
|
||||
fmt.Fprintf(w, "%s:1: %s %s\n", linkNote.Filename, prefix, linkNote.Title)
|
||||
}
|
||||
}
|
||||
prefix = fmt.Sprintf("%sincoming%s", opts.color.Code, opts.color.Reset)
|
||||
for _, link := range note.IncomingLinks {
|
||||
linkNote, exists := noteCache[link.Filename]
|
||||
if exists {
|
||||
fmt.Fprintf(w, "%s:%d: %s %s\n", linkNote.Filename, link.LineNumber, prefix, linkNote.Title)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch opts.limit {
|
||||
case "dangling":
|
||||
for _, note := range noteCache {
|
||||
for _, link := range note.OutgoingLinks {
|
||||
_, exists := noteCache[link.Filename]
|
||||
if !exists {
|
||||
fmt.Fprintf(w, "%s:%d: %s%s%s → %s\n", note.Filename, link.LineNumber, opts.color.Code, note.Title, opts.color.Reset, link.Filename)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
notes := getSortedNotes("alpha")
|
||||
for _, note := range notes {
|
||||
for _, link := range note.OutgoingLinks {
|
||||
linkNote, exists := noteCache[link.Filename]
|
||||
linkTitle := link.Filename
|
||||
if exists {
|
||||
linkTitle = linkNote.Title
|
||||
}
|
||||
fmt.Fprintf(w, "%s:%d: %s%s%s → %s\n", note.Filename, link.LineNumber, opts.color.Code, note.Title, opts.color.Reset, linkTitle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func notesiumLines(dir string, opts linesOptions, w io.Writer) {
|
||||
files, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not read directory: %s\n", err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && strings.HasSuffix(file.Name(), ".md") {
|
||||
filename := file.Name()
|
||||
path := filepath.Join(dir, filename)
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not open file: %s\n", err)
|
||||
}
|
||||
|
||||
var title string
|
||||
scanner := bufio.NewScanner(file)
|
||||
lineNumber := 0
|
||||
for scanner.Scan() {
|
||||
lineNumber++
|
||||
line := scanner.Text()
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if opts.prefix == "title" && title == "" && strings.HasPrefix(line, "# ") {
|
||||
title = strings.TrimPrefix(line, "# ")
|
||||
}
|
||||
if opts.filter != "" {
|
||||
matches, err := evaluateFilterQuery(opts.filter, line)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "Error: %v\n", err)
|
||||
continue
|
||||
}
|
||||
if !matches {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if opts.prefix == "title" {
|
||||
fmt.Fprintf(w, "%s:%d: %s%s%s %s\n", filename, lineNumber, opts.color.Code, title, opts.color.Reset, line)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s:%d: %s\n", filename, lineNumber, line)
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
log.Fatalf("scanner error: %s", err)
|
||||
}
|
||||
|
||||
file.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func notesiumStats(dir string, opts statsOptions, w io.Writer) {
|
||||
populateCache(dir)
|
||||
|
||||
labels := 0
|
||||
orphans := 0
|
||||
links := 0
|
||||
dangling := 0
|
||||
lines := 0
|
||||
words := 0
|
||||
chars := 0
|
||||
|
||||
for _, note := range noteCache {
|
||||
if note.IsLabel {
|
||||
labels++
|
||||
}
|
||||
if len(note.OutgoingLinks) == 0 && len(note.IncomingLinks) == 0 {
|
||||
orphans++
|
||||
}
|
||||
for _, link := range note.OutgoingLinks {
|
||||
_, exists := noteCache[link.Filename]
|
||||
if !exists {
|
||||
dangling++
|
||||
}
|
||||
}
|
||||
|
||||
links += len(note.OutgoingLinks)
|
||||
lines += note.Lines
|
||||
words += note.Words
|
||||
chars += note.Chars
|
||||
}
|
||||
|
||||
keyFormat := opts.color.Code + (map[bool]string{true: "%-9s", false: "%s"}[opts.table]) + opts.color.Reset
|
||||
fmt.Fprintf(w, keyFormat+" %d\n", "notes", len(noteCache))
|
||||
fmt.Fprintf(w, keyFormat+" %d\n", "labels", labels)
|
||||
fmt.Fprintf(w, keyFormat+" %d\n", "orphans", orphans)
|
||||
fmt.Fprintf(w, keyFormat+" %d\n", "links", links)
|
||||
fmt.Fprintf(w, keyFormat+" %d\n", "dangling", dangling)
|
||||
fmt.Fprintf(w, keyFormat+" %d\n", "lines", lines)
|
||||
fmt.Fprintf(w, keyFormat+" %d\n", "words", words)
|
||||
fmt.Fprintf(w, keyFormat+" %d\n", "chars", chars)
|
||||
}
|
||||
|
||||
func notesiumFinder(dir string, opts finderOptions) {
|
||||
inputCmd, err := parseOptions(opts.input)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
theme := os.Getenv("NOTESIUM_FINDER_THEME")
|
||||
var fzfColor string
|
||||
switch theme {
|
||||
case "light":
|
||||
fzfColor = "bg:15,bg+:7,fg:241,fg+:241,hl:4,hl+:2,pointer:9,info:6"
|
||||
default:
|
||||
fzfColor = "bg:8,bg+:0,fg:12,fg+:12,hl:11,hl+:3,pointer:9,info:3"
|
||||
}
|
||||
|
||||
optsFzf := []string{
|
||||
"--ansi",
|
||||
"--exact",
|
||||
"--border",
|
||||
"--reverse",
|
||||
"--no-sort",
|
||||
"--no-height",
|
||||
"--no-scrollbar",
|
||||
"--no-separator",
|
||||
"--pointer=>",
|
||||
"--delimiter=:",
|
||||
"--with-nth=3..",
|
||||
fmt.Sprintf("--color=%s", fzfColor),
|
||||
fmt.Sprintf("--prompt=%s> ", opts.prompt),
|
||||
}
|
||||
|
||||
if opts.preview {
|
||||
executablePath, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Fatalf("error resolving executable path: %v", err)
|
||||
}
|
||||
|
||||
executablePath, err = filepath.EvalSymlinks(executablePath)
|
||||
if err != nil {
|
||||
log.Fatalf("error resolving symlinked executable path: %v", err)
|
||||
}
|
||||
|
||||
optsFzf = append(optsFzf,
|
||||
"--bind=ctrl-/:toggle-preview",
|
||||
"--preview-window=+{2}-/2",
|
||||
fmt.Sprintf("--preview=%s cat {}", executablePath),
|
||||
)
|
||||
}
|
||||
|
||||
inputChan := make(chan string)
|
||||
go func() {
|
||||
defer close(inputChan)
|
||||
writer := &channelWriter{ch: inputChan}
|
||||
|
||||
switch inputCmd.Name {
|
||||
case "list":
|
||||
notesiumList(dir, inputCmd.Options.(listOptions), writer)
|
||||
case "links":
|
||||
notesiumLinks(dir, inputCmd.Options.(linksOptions), writer)
|
||||
case "lines":
|
||||
notesiumLines(dir, inputCmd.Options.(linesOptions), writer)
|
||||
default:
|
||||
log.Fatal("input command not supported: ", inputCmd.Name)
|
||||
}
|
||||
}()
|
||||
|
||||
results, code, err := runFinder(inputChan, optsFzf)
|
||||
if code != 0 && code != 130 && err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error running fzf: %v\n", err)
|
||||
}
|
||||
for _, line := range results {
|
||||
fmt.Print(line)
|
||||
}
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func notesiumCat(dir string, opts catOptions) {
|
||||
path := filepath.Join(dir, opts.filename)
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
log.Fatalf("Error opening file: %v\n", err)
|
||||
}
|
||||
defer file.Close()
|
||||
renderMarkdown(file, os.Stdout, opts.lineNumber)
|
||||
}
|
||||
|
||||
func notesiumWeb(dir string, opts webOptions) {
|
||||
populateCache(dir)
|
||||
|
||||
var httpfs http.FileSystem
|
||||
|
||||
if opts.webroot == "embedded" {
|
||||
subfs, err := fs.Sub(embedfs, embedfsWebRoot)
|
||||
if err != nil {
|
||||
log.Fatalf("embedded webroot sub error: %v", err)
|
||||
}
|
||||
httpfs = http.FS(subfs)
|
||||
} else {
|
||||
httpfs = http.Dir(opts.webroot)
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", opts.host, opts.port))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to listen on a port: %v", err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
url := "http://localhost:" + strings.Split(ln.Addr().String(), ":")[1]
|
||||
server := &http.Server{
|
||||
Addr: ln.Addr().String(),
|
||||
}
|
||||
|
||||
http.Handle("/", heartbeatH(http.FileServer(httpfs)))
|
||||
http.HandleFunc("/api/notes", heartbeatF(apiList))
|
||||
http.HandleFunc("/api/notes/", heartbeatF(func(w http.ResponseWriter, r *http.Request) {
|
||||
apiNote(dir, w, r, opts.readOnly)
|
||||
}))
|
||||
|
||||
http.HandleFunc("/api/runtime", heartbeatF(func(w http.ResponseWriter, r *http.Request) {
|
||||
apiRuntime(dir, w, r, opts)
|
||||
}))
|
||||
|
||||
http.HandleFunc("/api/raw/", heartbeatF(func(w http.ResponseWriter, r *http.Request) {
|
||||
apiRaw(dir, w, r)
|
||||
}))
|
||||
|
||||
for uri, srcDir := range opts.mounts {
|
||||
mountfs := http.Dir(srcDir)
|
||||
http.Handle(uri, http.StripPrefix(uri, heartbeatH(http.FileServer(mountfs))))
|
||||
}
|
||||
|
||||
var idleStopMsg string
|
||||
if opts.heartbeat {
|
||||
idleStopMsg = " (stop-on-idle enabled)"
|
||||
http.HandleFunc("/api/heartbeat", heartbeatF(apiHeartbeat))
|
||||
go checkHeartbeat(server)
|
||||
}
|
||||
|
||||
if opts.launchBrowser {
|
||||
go func() {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
cmd = exec.Command("xdg-open", url)
|
||||
case "darwin":
|
||||
cmd = exec.Command("open", url)
|
||||
case "windows":
|
||||
cmd = exec.Command("cmd", "/c", "start", url)
|
||||
default:
|
||||
log.Println("Unsupported OS for launching browser")
|
||||
return
|
||||
}
|
||||
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
log.Printf("Failed to launch the browser: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
fmt.Printf("Serving on %s (bind address %s)\n", url, opts.host)
|
||||
fmt.Printf("Press Ctrl+C to stop%s\n", idleStopMsg)
|
||||
if err := server.Serve(ln); err != http.ErrServerClosed {
|
||||
log.Fatalf("Server closed unexpected:%+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func notesiumExtract(opts extractOptions) {
|
||||
switch opts.path {
|
||||
case "":
|
||||
var files []string
|
||||
fs.WalkDir(embedfs, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !d.IsDir() {
|
||||
files = append(files, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
for _, file := range files {
|
||||
fmt.Println(file)
|
||||
}
|
||||
|
||||
default:
|
||||
content, err := fs.ReadFile(embedfs, opts.path)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read file: %s", err)
|
||||
}
|
||||
fmt.Println(string(content))
|
||||
}
|
||||
}
|
||||
|
||||
func notesiumVersion(opts versionOptions, w io.Writer) {
|
||||
version := getVersion(gitversion)
|
||||
|
||||
if opts.check {
|
||||
latest, err := getLatestReleaseInfo()
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "Error getting latest release info: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "Notesium %s (%s/%s)\n", version, runtime.GOOS, runtime.GOARCH)
|
||||
|
||||
comparison := compareVersions(version, latest.Version)
|
||||
switch comparison {
|
||||
case -1:
|
||||
publishedAt := latest.PublishedAt
|
||||
if parsedTime, err := time.Parse(time.RFC3339, latest.PublishedAt); err == nil {
|
||||
publishedAt = parsedTime.Local().Format("2006-01-02 15:04")
|
||||
}
|
||||
fmt.Fprintf(w, "A new release is available: %s (%s)\n", latest.Version, publishedAt)
|
||||
fmt.Fprintf(w, "https://github.com/alonswartz/notesium/releases\n")
|
||||
case 0:
|
||||
fmt.Fprintf(w, "You are using the latest version\n")
|
||||
case 1:
|
||||
fmt.Fprintf(w, "You are using a newer version than latest: %s\n", latest.Version)
|
||||
}
|
||||
|
||||
if opts.verbose {
|
||||
fmt.Fprintf(w, "\ncomparison:%d\n", comparison)
|
||||
fmt.Fprintf(w, "version:%s\n", version)
|
||||
fmt.Fprintf(w, "gitversion:%s\n", gitversion)
|
||||
fmt.Fprintf(w, "buildtime:%s\n", buildtime)
|
||||
fmt.Fprintf(w, "platform:%s/%s\n", runtime.GOOS, runtime.GOARCH)
|
||||
fmt.Fprintf(w, "latest.version:%s\n", latest.Version)
|
||||
fmt.Fprintf(w, "latest.published:%s\n", latest.PublishedAt)
|
||||
fmt.Fprintf(w, "latest.release:%s\n", latest.HTMLURL)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if opts.verbose {
|
||||
fmt.Fprintf(w, "version:%s\n", version)
|
||||
fmt.Fprintf(w, "gitversion:%s\n", gitversion)
|
||||
fmt.Fprintf(w, "buildtime:%s\n", buildtime)
|
||||
fmt.Fprintf(w, "platform:%s/%s\n", runtime.GOOS, runtime.GOARCH)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\n", version)
|
||||
}
|
||||
}
|
||||
|
||||
func getDateStamp(t time.Time, dateFormat string) string {
|
||||
dateStamp := t.Format(dateFormat)
|
||||
|
||||
// experimental: monday first day of week
|
||||
if strings.Contains(dateStamp, "%V") {
|
||||
_, week := t.ISOWeek()
|
||||
dateStamp = strings.Replace(dateStamp, "%V", fmt.Sprintf("%02d", week), 1)
|
||||
}
|
||||
|
||||
// experimental: sunday first day of week
|
||||
if strings.Contains(dateStamp, "%U") {
|
||||
_, week := t.ISOWeek()
|
||||
if t.Weekday() == time.Sunday {
|
||||
_, week = t.AddDate(0, 0, 1).ISOWeek()
|
||||
if t.Month() == time.January && t.Day() == 1 {
|
||||
week = 1
|
||||
}
|
||||
}
|
||||
dateStamp = strings.Replace(dateStamp, "%U", fmt.Sprintf("%02d", week), 1)
|
||||
}
|
||||
|
||||
return dateStamp
|
||||
}
|
||||
Reference in New Issue
Block a user