/router.go Secret
Created
October 23, 2021 05:44
Revisions
-
benhoyt created this gist
Oct 23, 2021 .There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,76 @@ /* From Yuri Vishnevsky on Gophers Slack: Hi Ben! I read your great post on Go routing. Based on the ideas there I wound up implementing my own router for a small site I'm building, which combines a few ideas from your post.Instead of passing a format string followed by pieces as in match ("foo/+/baz", &bar) I decided to inline the pieces, so the match arguments read as the path does: match("foo", &bar, "baz"). I'm curious if you have any thoughts, since this seems quite nice to me, and has a simple implementation. No worries if you're too busy to think about this right now; I just thought you might find it interesting. */ package main import ( "fmt" "strconv" "strings" ) // match reports whether `path` matches the given `pieces` // and assigns pointer pieces. A piece can be a string, // *string, or *int64. This function matches pieces greedily // and may assign pieces even when the path does not match. // Note: Does not normalize paths with path.Clean. // Note: Consecutive string path components need to be matched // with separate strings, since this always splits on /. func match(path string, pieces ...interface{}) bool { // Remove the initial "/" prefix if strings.HasPrefix(path, "/") { path = path[1:] } var head string for i, piece := range pieces { // Shift the next path component into `head` head, path = nextComponent(path) // Match pieces based on their type switch p := piece.(type) { case string: // Match a specific string if p != head { return false } case *string: // Match any string, including the empty string *p = head case *int64: // Match any 64-bit integer, including negative integers n, err := strconv.ParseInt(head, 10, 64) if err != nil { return false } *p = n default: panic(fmt.Sprintf("each piece must be a string, *string, or *int64. Got %T", piece)) } // If the path is fully consumed, we're done if pieces are also fully consumed if path == "" { return i == len(pieces)-1 } } // Pieces are consumed; return true if the path is too. return path == "" } // Accepts a path without leading slash and returns two strings: // its first component and the rest without leading slash func nextComponent(path string) (head, tail string) { i := strings.IndexByte(path, '/') if i == -1 { return path, "" } return path[:i], path[i+1:] }