Given two absolute URI, find the relative path between them - go

Is there a function in go standard library that lets me do this
a = 'www.my.com/your/stuff'
b = 'www.my.com/your/stuff/123/4'
function(b,a) // /123/4
or
function(URL(b),URL(a)) // /123/4
The following is probably defined in this case
function(a,b) // error ? or ../../
I'm aware that I can use path package for this. But it cannot work in many cases where there is query param, file extension etc.
Basically I'm looking for a path.resolve counterpart for URL

It turns out that the path/filepath package can do this for you. If you ignore the fact that these are URLs and instead treat them like paths, you can use filepath.Rel():
package main
import (
"fmt"
"path/filepath"
)
func main() {
base := "www.my.com/your/stuff"
target := "www.my.com/your/stuff/123/4"
rel, _ := filepath.Rel(base, target)
fmt.Println(rel) // prints "123/4"
}
Playground: https://play.golang.org/p/nnF9zfFAFfc
If you want to treat these paths as actual URLs, you should probably use the net/url package to first parse the path as a URL, then extract the path and use filepath.Rel() on that. This allows you to properly deal with things like queries in the URL string, which would trip up filepath, like so:
package main
import (
"fmt"
"path/filepath"
"net/url"
)
func main() {
url1, _ := url.Parse("http://www.my.com/your/stuff")
url2, _ := url.Parse("http://www.my.com/your/stuff/123/4?query=test")
base := url1.Path
target := url2.Path
rel, _ := filepath.Rel(base, target)
fmt.Println(base) // "/your/stuff"
fmt.Println(target) // "/your/stuff/123/4"
fmt.Println(rel) // "123/4"
}
Playground: https://play.golang.org/p/gnZfk0t8GOZ
As a bonus, filepath.Rel() is smart enough to handle relative paths in the other direction, too:
rel, _ = filepath.Rel(target, base) // rel is now "../.."

Related

How to check if a public go function/struct is not used outside of the package?

Is there a way to check if a public function/struct is used outside of the package in which it's declared? I'm not writing a public go module that's consumed anywhere else, and simply want to scan whether func Foo() it's used anywhere in my codebase outside of the package in which it's declared.
I'm using GoLand but any programmatic solution would do.
Simplest solution: manually rename Foo() to Foo2(). Build/compile your project: if there are no compilation errors, it's not referenced in your code. Same check also works with any identifiers and with any IDEs (this doesn't use any of the IDE's features).
Obviously if you already have a Foo2 identifier, this will fail. But the idea is to rename it to a non-existing identifier...
You can scan a particular package to see all the available function in it.
In this main.go, app the root package name and there is another package in database directory under the package name database.
By running the code you will found all the function name available inside database package
package main
import (
"fmt"
"app/database"
"go/ast"
"go/parser"
"go/token"
"os"
)
// Name of the package you want to scan
const subPackage = "database"
func main() {
set := token.NewFileSet()
packs, err := parser.ParseDir(set, subPackage, nil, 0)
if err != nil {
fmt.Println("Failed to parse package:", err)
os.Exit(1)
}
funcs := []*ast.FuncDecl{}
for _, pack := range packs {
for _, f := range pack.Files {
for _, d := range f.Decls {
if fn, isFn := d.(*ast.FuncDecl); isFn {
funcs = append(funcs, fn)
}
}
}
}
fmt.Println("All the functions in the package:",subPackage)
for _, fn := range funcs {
fmt.Println(fn.Name.Name)
}
// database Package is called/used
database.Connection()
}
This will get all function declarations in the stated subpackage as an ast.FuncDecl. This isn't an invokable function; it's just a representation of its source code of it.
If you wanted to do anything like call these functions, you'd have to do something more sophisticated. After gathering these functions, you could gather them and output a separate file that calls each of them, then run the resulting file.

Does go provide variable sanitization?

I am a beginner in Golang.
I have a problem with variable type assigning from user input.
When the user enters data like "2012BV352" I need to be able to ignore the BV and pass 2012352 to my next function.
There has a package name gopkg.in/validator.v2 in doc
But what it returns is whether or not the variable is safe or not.
I need to cut off the unusual things.
Any idea on how to achieve this?
You could write your own sanitizing methods and if it becomes something you'll be using more often, I'd package it out and add other methods to cover more use cases.
I provide two different ways to achieve the same result. One is commented out.
I haven't run any benchmarks so i couldn't tell you for certain which is more performant, but you could write your own tests if you wanted to figure it out. It would also expose another important aspect of Go and in my opinion one of it's more powerful tools... testing.
package main
import (
"fmt"
"log"
"regexp"
"strconv"
"strings"
)
// using a regex here which simply targets all digits and ignores everything else. I make it a global var and use MustCompile because the
// regex doesn't need to be created every time.
var extractInts = regexp.MustCompile(`\d+`)
func SanitizeStringToInt(input string) (int, error) {
m := extractInts.FindAllString(input, -1)
s := strings.Join(m, "")
return strconv.Atoi(s)
}
/*
// if you didn't want to use regex you could use a for loop
func SanitizeStringToInt(input string) (int, error) {
var s string
for _, r := range input {
if !unicode.IsLetter(r) {
s += string(r)
}
}
return strconv.Atoi(s)
}
*/
func main() {
a := "2012BV352"
n, err := SanitizeStringToInt(a)
if err != nil {
log.Fatal(err)
}
fmt.Println(n)
}

What is the idiomatic way to read urls with a file scheme as filenames for ReadFile?

Is there an idiomatic way to read a file from the system starting from a (file scheme) url and not a path?
I tried this first:
fileUrlStr := "file:///path/to/file.json"
jsonBuffer, _ := ioutil.ReadFile(fileUrlStr)
This is my current (mostly working version) but I'm concerned there are some gotchas that I'm missing, so I'm hoping there's a more tried and true way to do it:
fileUrlStr := "file:///path/to/file.json"
fileUrl, _ := url.Parse(fileUrlStr)
jsonBuffer, _ := ioutil.ReadFile(fileUrl.Path)
(Bonus if I can support both file:///Users/jdoe/temp.json and file:///c:/WINDOWS/clock.json without having to add code-paths accounting for them)
Using net/url, the solution that you were using, is the correct one.
It's properly deals with hostnames and paths across platforms and also gives you a chance to check the url scheme is the file scheme.
package main
import (
"fmt"
"net/url"
)
func main() {
for _, path := range []string{
"file:///path/to/file.json",
"file:///c:/WINDOWS/clock.json",
"file://localhost/path/to/file.json",
"file://localhost/c:/WINDOWS/clock.avi",
// A case that you probably don't need to handle given the rarity,
// but is a known legacy win32 issue when translating \\remotehost\share\dir\file.txt
"file:////remotehost/share/dir/file.txt",
} {
u, _ := url.ParseRequestURI(path)
fmt.Printf("url:%v\nscheme:%v host:%v Path:%v\n\n", u, u.Scheme, u.Host, u.Path)
}
}

Get the first directory of a path in GO

In Go, is it possible to get the root directory of a path so that
foo/bar/file.txt
returns foo? I know about path/filepath, but
package main
import (
"fmt"
"path/filepath"
)
func main() {
parts := filepath.SplitList("foo/bar/file.txt")
fmt.Println(parts[0])
fmt.Println(len(parts))
}
prints foo/bar/file.txt and 1 whereas I would have expected foo and 3.
Simply use strings.Split():
s := "foo/bar/file.txt"
parts := strings.Split(s, "/")
fmt.Println(parts[0], len(parts))
fmt.Println(parts)
Output (try it on the Go Playground):
foo 3
[foo bar file.txt]
Note:
If you want to split by the path separator of the current OS, use os.PathSeparator as the separator:
parts := strings.Split(s, string(os.PathSeparator))
filepath.SplitList() splits multiple joined paths into separate paths. It does not split one path into folders and file. For example:
fmt.Println("On Unix:", filepath.SplitList("/a/b/c:/usr/bin"))
Outputs:
On Unix: [/a/b/c /usr/bin]
Note that if you just need the first part, strings.SplitN is at least 10 times
faster from my testing:
package main
import "strings"
func main() {
parts := strings.SplitN("foo/bar/file.txt", "/", 2)
println(parts[0] == "foo")
}
https://golang.org/pkg/strings#SplitN

How do I parse URLs in the format of /id/123 not ?foo=bar

I'm trying to parse an URL like:
http://example.com/id/123
I've read through the net/url docs but it seems like it only parses strings like
http://example.com/blah?id=123
How can I parse the ID so I end up with the value of the id in the first example?
This is not one of my own routes but a http string returned from an openid request.
In your example /id/123 is a path and you can get the "123" part by using Base from the path module.
package main
import (
"fmt"
"path"
)
func main() {
fmt.Println(path.Base("/id/123"))
}
For easy reference, here's the docs on the path module. http://golang.org/pkg/path/#example_Base
You can try using regular expression as follow:
import "regexp"
re, _ := regexp.Compile("/id/(.*)")
values := re.FindStringSubmatch(path)
if len(values) > 0 {
fmt.Println("ID : ", values[1])
}
Here is a simple solution that works for URLs with the same structure as yours (you can improve to suit those with other structures)
package main
import (
"fmt"
"net/url"
)
var path = "http://localhost:8080/id/123"
func getFirstParam(path string) (ps string) {
// ignore first '/' and when it hits the second '/'
// get whatever is after it as a parameter
for i := 1; i < len(path); i++ {
if path[i] == '/' {
ps = path[i+1:]
}
}
return
}
func main() {
u, _ := url.Parse(path)
fmt.Println(u.Path) // -> "/id/123"
fmt.Println(getFirstParam(u.Path)) // -> "123"
}
Or, as #gollipher suggested, use the path package
import "path"
func main() {
u, _ := url.Parse(path)
ps := path.Base(u.Path)
}
With this method it's faster than regex, provided you know before hand the structure of the URL you are getting.

Resources