Skip to content

jeftadlvw/go-pathlib

Repository files navigation

Artwork for go-pathlib.

go-pathlib

A simple one-file library for handling filesystem paths. Utilizing Golang's path/filepath, inspired by Python's pathlib API. It abstracts the standard library by providing a simple immutable struct that acts as a source of truth for file paths.

This library is developed and tested on Unix-based operating systems. Windows should work (in theory), please open an issue if you face any problems.

Minimum tested Go version: 1.22

Current State: stabilizing API, tested across multiple platforms (Linux, macOS and Windows), API scope is not expected to be reduced. More see Roadmap.

Getting Started 🚀

go get github.com/jeftadlvw/go-pathlib@latest
package main

import (
	"fmt"
	"github.com/jeftadlvw/go-pathlib/pathlib"
)

func main() {
	p := pathlib.NewPath("path/to/your/destination")
	fmt.Println(p)
}

Features ✨

  • represent file paths as an own struct type with minimal memory overhead
  • many functions for handling file paths
  • implements encoder.TextMarshaler and encoder.TextUnmashaler, enabling automatic integration into first- and third-party parsing libraries (for e.g. text or JSON)

API Documentation 📝

Repository-local documentation can be found at docs/go-pathlib.md. It's auto-generated by gomarkdoc using the docstrings in the source code.

The file is updated regularly and gives a good general overview on the API. Up-to-date documentation can be found in the source code.

Roadmap 📋

🔖 Current version: 0.0.4

The API stabilized and is covered by many test cases. Tests are run on Linux, macOS and Windows. The current API scope is not expected to be modified or reduced. The only thing the library needs now is real-world usage in projects to catch missed cross-platform errors and refine the look-and-feel of the APIs. When the library has been tested and evaluated in other projects over a longer time, a v1 release is more likely.

Next features are planned and fixed on the roadmap for the next release(s). They extend and improve API semantics and internals and make the library more mature. Future features are planned, but not focussed on being implemented next per sé. They might, but are not prioritized.

Next

Future

  • parallelize recursive filesystem walking and globbing
  • implement "range over function" for globbing (requires newer go versions)

Planned extensions

  • integration into go-validator (custom field types and validators)

This is a non-exhaustive list. Feel free to suggest other features and integrations!

Guides and Rationales 🌚

Posix vs. Windows path representation

Internally, all paths are stored in a canonical Posix form (forward slashes, Windows anchors separated out). This keeps the core logic simple and platform-independent.

Constructors:

  • NewPath() is the safe default. It detects the current platform and handles Windows volume names and UNC roots automatically by branching to either NewPathFromPosix() or NewPathFromWindows(). Use it for path strings from any source, including OS APIs (e.g. os.Getwd(), filepath.EvalSymlinks()).
  • NewPathFromPosix() assumes a Posix path and performs no Windows anchor parsing.
  • NewPathFromWindows() always applies separator replacement (\\/) and Windows anchor parsing regardless of the platform. If no Windows anchor is detected, paths behave like regular canonical posix paths.

Output follows the principle of platform-native by default, explicit when needed:

  • String() returns the platform-native representation (backslashes on Windows, forward slashes on Posix) by branching to either ToPosix() or ToWindows(). This is what you pass to OS APIs like os.Open().
  • ToPosix() always returns forward slashes. Use this for serialization and cross-platform storage. Windows anchors are included (including the double slash for UNC paths), but formatted as forward slashes.
  • ToWindows() always returns backslashes. Use this when generating Windows paths on any platform.

Methods that return path components (Anchor(), Base(), Parts(), Split(), etc.) follow the same platform-native convention where separators are visible (e.g. Anchor() returns \\host\share on Windows, //host/share on Posix).

\ on Posix vs \ on Windows

Backslashes (\) are allowed in regular Posix path part names, but are seen as path separators in Windows paths. Thus, a path string containing a backslash is interpreted differently on different operating systems. This is a problem if a filepath with backslashes is persisted using Posix and read/used on Windows.

There is really nothing to prevent this behavior, which is why this library prints a warning to stderr if a Posix path string contains a backslash. You can disable these warnings by setting pathlib.PrintBackslashWarningOnPosix to false.

Persisting file paths

When persisting file paths in e.g. configuration files or a database, use lowercase paths and use the posix representation for maximum portability. Also persist a path relative to some base path, and resolve the absolute path at runtime.

Enforce that the start and end of a path are clearly defined to escape whitespace usage. If you store paths in a database, then the path is naturally constrained by the database field. But when persisted in e.g. a configuration file, enclose the path with e.g. quotation marks: "path/to/foo.bar".

Path equality and case sensitivity

Don't assume the underlying filesystem is case-insensitive. This is the case for Windows and MacOS, but not for e.g. Linux.

Whether you design the paths in your application to be case-sensitive or not is your decision. But keep in mind that case sensitivity also comes with path ambiguity (e.g.: should file.txt and FILE.txt be treated equally?).

Although we recommend handling paths in a case-insensitive manner, we respect stricter designs and follow the principle of being strict by default, while allowing flexibility explicitly. We provide functions to check for path equality:

  • Equals: default, lexical, case-sensitive by default, but switchable with flag
  • EqualsString: convenience wrapper for Equals, where the argument is interpreted as a canonical Posix string. Reserve it for strings you control, such as ToPosix() output or serialized config, not OS-native strings like String(). For those, parse first: p.Equals(NewPath(s)).
  • EqualsFs: filesystem equality (only in pathlib_fs.go)

Contributing 👥

Feel free to open issues and pull requests. Any help or feedback is highly appreciated!

Attributions 🖊️

See NOTICE.md for a complete list of used third party projects and source code.

The displayed Gopher in the artwork is licensed under the Creative Commons 4.0 Attribution License as per https://go.dev/brand#logo (last seen: 30-09-2024). This project's artwork falls under the same licence.

About

A Golang library for handling filesystem paths. Inspired by Python's pathlib.

Topics

Resources

License

Stars

2 stars

Watchers

1 watching

Forks

Contributors