Skip to main content

initial commit

ID
a3d8c84
date
2025-09-28 21:06:36+00:00
author
Alex Chan <alexc@tailscale.com>
message
initial commit
changed files
1 file, 108 additions

Changed files

q.go (0) → q.go (2236)

diff --git a/q.go b/q.go
new file mode 100644
index 0000000..3300804
--- /dev/null
+++ b/q.go
@@ -0,0 +1,108 @@
+package q
+
+import (
+	"bufio"
+	"fmt"
+	"os"
+	"runtime"
+	"strings"
+)
+
+func getFunctionName() string {
+	pc, _, _, ok := runtime.Caller(2)
+	if !ok {
+		return "<unknown>"
+	}
+
+	fn := runtime.FuncForPC(pc)
+	if fn == nil {
+		return "<unknown>"
+	}
+
+	// The name of test functions can be long and have multiple parts,
+	// e.g. tailscale.com/wgengine/magicsock.TestNetworkDownSendErrors
+	//
+	// For brevity, just get the last part.
+	parts := strings.Split(fn.Name(), ".")
+	lastPart := parts[len(parts)-1]
+
+	return lastPart
+}
+
+func getExpression() string {
+	_, file, line, ok := runtime.Caller(2)
+	if !ok {
+		return "<unknown>"
+	}
+
+	f, err := os.Open(file)
+	if err != nil {
+		return "<unknown>"
+	}
+	defer f.Close()
+
+	scanner := bufio.NewScanner(f)
+	currentLine := 1
+
+	for scanner.Scan() {
+		if currentLine == line {
+			thisLine := strings.TrimSpace(scanner.Text())
+			thisLine, _ = strings.CutPrefix(thisLine, "q.Q(")
+			thisLine, _ = strings.CutSuffix(thisLine, ")")
+			return thisLine
+		}
+		currentLine++
+	}
+
+	if err := scanner.Err(); err != nil {
+		return "<unknown>"
+	}
+
+	return "<unknown>"
+}
+
+func toString(value any, a ...any) string {
+	switch v := value.(type) {
+	case string:
+		if len(a) == 0 {
+			return fmt.Sprintf("%q", v)
+		} else {
+			v = strings.ReplaceAll(v, "%+v", "\x1b[39m%+v\x1b[39m")
+			v = strings.ReplaceAll(v, "%v", "\x1b[39m%v\x1b[39m")
+			v = strings.ReplaceAll(v, "%t", "\x1b[39m%t\x1b[39m")
+			return fmt.Sprintf(v, a...)
+		}
+	case int, int8, int16, int32, int64,
+		uint, uint8, uint16, uint32, uint64,
+		float32, float64, bool:
+		return fmt.Sprintf("%v", v)
+	case fmt.Stringer:
+		return v.String()
+	default:
+		return fmt.Sprintf("%v", v) // fallback
+	}
+}
+
+func Q(value any, a ...any) {
+	f, err := os.OpenFile("/tmp/q.txt", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
+	if err != nil {
+		panic(err)
+	}
+	defer f.Close()
+
+	functionName := getFunctionName()
+	expression := getExpression()
+
+	var line string
+
+	switch value.(type) {
+	case string:
+		line = "\x1b[32m" + functionName + "\x1b[39m: " + toString(value, a...) + "\n\n"
+	default:
+		line = "\x1b[32m" + functionName + "\x1b[39m: " + expression + " = \x1b[36m" + toString(value, a...) + "\x1b[39m\n\n"
+	}
+
+	if _, err = f.WriteString(line); err != nil {
+		panic(err)
+	}
+}