Tutorial: Getting started with generics - The Go Programming Language â Okay @xuu@txt.sour.is I quite like Goâs generics now 𤣠After going through this myself I like the semantics and the syntax. Iâm glad they did a lot of work on this to keep it simple to both understand and use (just like the rest of Go) đ
#GoLang #Generics
@prologic@twtxt.net its only a Pre-JR dev level.
ahh this is useful https://go.dev/doc/modules/managing-dependencies. the go culture doesnât typically have large dependency graphs like Ruby or JS.
I love parsers in go : https://dev-nonsense.com/posts/incremental-parsing-in-go/
Bueno, siento que este twtxt ha sido como un micro-journaling para pensamientos personales, aunque quizĂĄs te guste o inspire algo de lo que cuento por acĂĄ.
Este pensamiento me vino inspirado por https://josias.dev/posts/justwrite/
@eaplmx@twtxt.net CSS Battle has a passwordless login, I donât know the implementation but it basically send you and email where you click a link and youâre in.
Todayâs âWhere in the Worldâ puzzle was tough. gemini://gemi.dev/cgi-bin/witw.cgi/play
@prologic@twtxt.net Error handling especially in Go is very tricky I think. Even though the idea is simple, itâs fairly hard to actually implement and use in a meaningful way in my opinion. All this error wrapping or the lack of it and checking whether some specific error occurred is a mess. errors.As(âŚ) just doesnât feel natural. errors.Is(âŚ) only just. I mainly avoided it. Yesterday evening I actually researched a bit about that and found this article on errors with Go 1.13. It shed a little bit of light, but I still have a long way to go, I reckon.
We tried several things but havenât found the holy grail. Currently, we have a mix of different styles, but nothing feels really right. And having plenty of different approaches also doesnât help, thatâs right. I agree, error messages often end up getting wrapped way too much with useless information. We havenât found a solution yet. We just noticed that it kind of depends on the exact circumstances, sometimes the caller should add more information, sometimes itâs better if the callee already includes what it was supposed to do.
To experiment and get a feel for yesterdayâs research results I tried myself on the combined log parser and how to signal three different errors. Iâm not happy with it. Any feedback is highly appreciated. The idea is to let the caller check (not implemented yet) whether a specific error occurred. That means I have to define some dedicated errors upfront (ErrInvalidFormat, ErrInvalidStatusCode, ErrInvalidSentBytes) that can be used in the err == ErrInvalidFormat or probably more correct errors.Is(err, ErrInvalidFormat) check at the caller.
All three errors define separate error categories and are created using errors.New(âŚ). But for the invalid status code and invalid sent bytes cases I want to include more detail, the actual invalid number that is. Since these errors are already predefined, I cannot add this dynamic information to them. So I would need to wrap them Ă la fmt.Errorf("invalid sent bytes '%s': %w", sentBytes, ErrInvalidSentBytes"). Yet, the ErrInvalidSentBytes is wrapped and can be asserted later on using errors.Is(err, ErrInvalidSentBytes), but the big problem is that the message is repeated. I donât want that!
Having a Python and Java background, exception hierarchies are a well understood concept Iâm trying to use here. While typing this long message it occurs to me that this is probably the issue here. Anyways, I thought, I just create a ParseError type, that can hold a custom message and some causing error (one of the three ErrInvalid* above). The custom message is then returned at Error() and the wrapped cause will be matched in Is(âŚ). I then just return a ParseError{fmt.Sprintf("invalid sent bytes '%s'", sentBytes), ErrInvalidSentBytes}, but that looks super weird.
I probably need to scrap the âparent errorâ ParseError and make all three âsuberrorsâ three dedicated error types implementing Error() string methods where I create a useful error messages. Then the caller probably could just errors.Is(err, InvalidSentBytesError{}). But creating an instance of the InvalidSentBytesError type only to check for such an error category just does feel wrong to me. However, it might be the way to do this. I donât know. To be tried. Opinions, anyone? Implementing a whole new type is some effort, that I want to avoid.
Alternatively just one ParseError containing an error kind enumeration for InvalidFormat and friends could be used. Also seen that pattern before. But that would then require the much more verbose var parseError ParseError; if errors.As(err, &parseError) && parseError.Kind == InvalidSentBytes { ⌠} or something like that. Far from elegant in my eyes.
Hmm, @prologic@twtxt.net / @lyse@lyse.isobeef.org: Should we remove the section âTraditional Human-Readable Topicsâ from the spec? Or mark is as deprecated? I havenât seen this being used in the wild for years. đ¤
@chronolink@chrono.tilde.cafe Replies are not part of the original twtxt format. They were added later as an extension by Yarn.social: https://dev.twtxt.net/doc/twtsubjectextension.html (only the section âMachine-Parsable Conversation Groupingâ is used these days)
This looks promising - https://lapce.dev
vim inspired and âlightning-fastâ. still in its early days.
#!/bin/sh
# Validate environment
if ! command -v msgbus > /dev/null; then
printf "missing msgbus command. Use: go install git.mills.io/prologic/msgbus/cmd/msgbus@latest"
exit 1
fi
if ! command -v salty > /dev/null; then
printf "missing salty command. Use: go install go.mills.io/salty/cmd/salty@latest"
exit 1
fi
if ! command -v salty-keygen > /dev/null; then
printf "missing salty-keygen command. Use: go install go.mills.io/salty/cmd/salty-keygen@latest"
exit 1
fi
if [ -z "$SALTY_IDENTITY" ]; then
export SALTY_IDENTITY="$HOME/.config/salty/$USER.key"
fi
get_user () {
user=$(grep user: "$SALTY_IDENTITY" | awk '{print $3}')
if [ -z "$user" ]; then
user="$USER"
fi
echo "$user"
}
stream () {
if [ -z "$SALTY_IDENTITY" ]; then
echo "SALTY_IDENTITY not set"
exit 2
fi
jq -r '.payload' | base64 -d | salty -i "$SALTY_IDENTITY" -d
}
lookup () {
if [ $# -lt 1 ]; then
printf "Usage: %s nick@domain\n" "$(basename "$0")"
exit 1
fi
user="$1"
nick="$(echo "$user" | awk -F@ '{ print $1 }')"
domain="$(echo "$user" | awk -F@ '{ print $2 }')"
curl -qsSL "https://$domain/.well-known/salty/${nick}.json"
}
readmsgs () {
topic="$1"
if [ -z "$topic" ]; then
topic=$(get_user)
fi
export SALTY_IDENTITY="$HOME/.config/salty/$topic.key"
if [ ! -f "$SALTY_IDENTITY" ]; then
echo "identity file missing for user $topic" >&2
exit 1
fi
msgbus sub "$topic" "$0"
}
sendmsg () {
if [ $# -lt 2 ]; then
printf "Usage: %s nick@domain.tld <message>\n" "$(basename "$0")"
exit 0
fi
if [ -z "$SALTY_IDENTITY" ]; then
echo "SALTY_IDENTITY not set"
exit 2
fi
user="$1"
message="$2"
salty_json="$(mktemp /tmp/salty.XXXXXX)"
lookup "$user" > "$salty_json"
endpoint="$(jq -r '.endpoint' < "$salty_json")"
topic="$(jq -r '.topic' < "$salty_json")"
key="$(jq -r '.key' < "$salty_json")"
rm "$salty_json"
message="[$(date +%FT%TZ)] <$(get_user)> $message"
echo "$message" \
| salty -i "$SALTY_IDENTITY" -r "$key" \
| msgbus -u "$endpoint" pub "$topic"
}
make_user () {
mkdir -p "$HOME/.config/salty"
if [ $# -lt 1 ]; then
user=$USER
else
user=$1
fi
identity_file="$HOME/.config/salty/$user.key"
if [ -f "$identity_file" ]; then
printf "user key exists!"
exit 1
fi
# Check for msgbus env.. probably can make it fallback to looking for a config file?
if [ -z "$MSGBUS_URI" ]; then
printf "missing MSGBUS_URI in environment"
exit 1
fi
salty-keygen -o "$identity_file"
echo "# user: $user" >> "$identity_file"
pubkey=$(grep key: "$identity_file" | awk '{print $4}')
cat <<- EOF
Create this file in your webserver well-known folder. https://hostname.tld/.well-known/salty/$user.json
{
"endpoint": "$MSGBUS_URI",
"topic": "$user",
"key": "$pubkey"
}
EOF
}
# check if streaming
if [ ! -t 1 ]; then
stream
exit 0
fi
# Show Help
if [ $# -lt 1 ]; then
printf "Commands: send read lookup"
exit 0
fi
CMD=$1
shift
case $CMD in
send)
sendmsg "$@"
;;
read)
readmsgs "$@"
;;
lookup)
lookup "$@"
;;
make-user)
make_user "$@"
;;
esac
I will never understand this obsession with âscalingâ. Modern web dev seriously over-complicates so many things, itâs not even funny anymore. How we built a serverless SQL database | Hacker News
dev snapshots of tomo el fuego are now available https://sunshinegardens.org/~xj9/wiki/tomo/ #tomo #inferno #plan9
So excited for Go embedded files. https://golangtutorial.dev/tips/embed-files-in-go/
tilde.club twtxt users: $ find /home -type d -name âpublic_htmlâ -exec find {} -type f \; 2>/dev/null | grep twtxt