Giter VIP home page Giter VIP logo

path-dsl-rs's Introduction

path-dsl

Latest version Documentation License

Utility DSL and macro to help deal with Paths.

PathDSL provides a simple and zero-overhead abstraction for creating paths and appending to existing Path-like things.

Overview

use path_dsl::path;
# use std::path::{PathBuf, Path};

// PathBuf::push() only called once with consecutive literals:
let literals: PathBuf = path!("dir1" | "dir2" | "dir3");
// Type annotation for illustration purposes; not needed

// Does not copy data if first path segment is a owning value:
let moving = path!(literals | "dir4");

// Mixing and matching is easy:
let start = path!("some" | "dir");
let end = path!("my_folder" | "my_file.txt");
// Can borrow as normal
let result = path!(start | "middle_folder" | &end);

// Works with PathBuf, Path, and String-likes
let file = Path::new("file.txt");
let folder = PathBuf::from("folder");
let middle: &str = "other_middle";
let combined = path!(folder | middle | "middle_folder" | file);

PathDSL Macro and Type

PathDSL's path! macro allows for the creation of a PathBuf in the most efficent way possible in the situation.

note the use of | instead of / due to rust's macro rules

use path_dsl::path;
// Type annotation for illustration only, not needed
let path: PathBuf = path!("dir1" | "dir2" | "dir3" | "file.txt");

PathDSL

You can also generate a PathDSL directly, though this is discouraged. PathDSL will pretend to be a PathBuf as best it can, but it is almost always more efficent to use the path! macro to generate a PathBuf directly.

use path_dsl::PathDSL;
let path = PathDSL::from("dir1") / "dir2" / "dir3" / "file.txt";

Adding Path-Like Structures

As well as using regular string literals, you can use anything that can be passed to PathBuf::push as a part of the DSL.

Note the borrow on other: as these types are not Copy, they will be moved into the path unless you borrow them. This matches behavior with PathBuf::push, but can be surprising when used in a infix expression.

use path_dsl::{path, PathDSL};

let other = PathBuf::from("some_dir");
let filename: &str = "my_file.txt";

let mac: PathBuf  = path!("dir1" | "dir2" | &other | filename); // Preferred
let path: PathDSL = PathDSL::from("dir1") / "dir2" / other / filename; // Also works

Moving vs Borrowing

Both the macro and the DSL type behave the same with regard to borrowing vs moving. If a reference is provided, it will borrow the provided value. However, if a value is provided it will move it, making the value unusable afterwards. While these are the normal rules for rust, infix operators are normally used with Copy types, so this may be surprising.

Both mutable and immutable borrows are supported, though they will never actually mutate anything.

use path_dsl::path;
# use std::path::PathBuf;

let value = PathBuf::from("some_dir");
let borrow: &str = "my_file.txt";

let mac  = path!(value | borrow);
let path = path!(value | borrow); // Will not compile because `value` was moved

You must manually borrow it:

let mac  = path!(&value | borrow); // Borrow value so it can be used later
let path = PathDSL::new() / value / borrow; // Not used afterwards, so doesn't need a borrow

PathDSL <=> PathBuf

PathDSL is designed to be a drop-in replacement for PathBuf, including trivial conversions between the two. In any situation where you would be able to use PathBuf you can use PathDSL. PathDSL includes an implementation of Deref to a PathBuf (and by proxy Path) and re-implements all functions that take self, so is fully api compatable. However there are some situations where you must have a PathBuf. Obtaining a &PathBuf is trivial through dereferencing and obtaining a PathBuf is possible through the PathDSL::into_pathbuf function.

PathDSL is #[repr(transparent)] over PathBuf and all functions are force-inlined so conversions and operations should be cost-free compared to the equivalent PathBuf operation. If they aren't, please file a bug.

Some known issues are:

Equality

use path_dsl::path;

let dsl = path!("file.txt");
let buf = PathBuf::from("file.txt");

assert!(dsl == buf);
// Must de-reference to PathBuf can't implement `Eq` for `PathBuf`
assert!(buf == *dsl);

Function Calls

use path_dsl::path;

fn func(p: PathBuf) {
}

let dsl = path!("file.txt");
let buf = PathBuf::from("file.txt");

func(buf);
// Must convert into `PathBuf`
// Dereferencing doesn't work because `func` moves.
func(dsl.to_path_buf());
func(dsl.into()) // also works

Macro Optimizations

As previously mentioned, the macro contains some optimizations over using raw PathDSL and should always be used over manually using PathDSL. These optimizations happen at compile time, and are guaranteed. Further details on these can be found on the path! macro documentation.

String Literal Concatenation:

While it is ill-advised to use string literals with slashes in a Path, The path! macro takes slashes into account, and automatically constructs a single string literal from multiple consecutive string literals. This can potentially save an allocation or two in the underlying OsString.

use path_dsl::path;

let p = path!("this" | "is" | "combined");
if cfg!(windows) {
    assert_eq!(p, PathBuf::from("this\\is\\combined"));
} else {
    assert_eq!(p, PathBuf::from("this/is/combined"));
}

First-Argument Optimization:

When the very first argument of the path! macro is a owning PathBuf, OsString or PathDSL passed by value (moved), instead of copying everything into a new PathDSL, it will just steal the buffer from that moved-in value. This allows you to use the path! macro fearlessly when appending to already existing variables.

use path_dsl::path;

let first = PathBuf::from("a_very_long_folder_name");
let p = path!(first); // Does not copy anything.

Why Use A Crate?

You may be wondering why you should use a crate for this when you can easily wrap PathBuf and add some Div implementations. This is basically what I thought as well until I actually went to go implement this crate. There is a surprising amount of very tedious and particular code to try to emulate PathBuf directly, as well as to test the functionality.

With this in mind, I have made path_dsl completely dependency free, choosing to lean on declarative macros over proc macros as to not depend on things like syn. Additionally, everything is contained within this one file, I have thorough tests, and I have added #[deny(unsafe_code)] for good measure. Hopefully this makes this crate light enough and easily-auditable enough to be an acceptable dependency.

License: MIT

path-dsl-rs's People

Contributors

cwfitzgerald avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

isgasho

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.