Skip to content

Next version #33

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 60 commits into from
Mar 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
aac9767
Add kinds RelOrAbs FileOrDir SandboxedOrNot
safareli Feb 9, 2018
1aec600
Try to change FileName and DirName to Name FileOrDir
safareli Feb 9, 2018
c2ba2c5
fix relativeTo
safareli Feb 11, 2018
4fcf61a
Use non empty string
safareli Feb 12, 2018
8bed445
remove maybe{Dir,File,Rel,Abs} is{Absolute,Relative}
safareli Feb 13, 2018
2d5b7f7
remove special version of peel
safareli Feb 13, 2018
18e1bee
dir or file info is in types only now
safareli Feb 13, 2018
08efb73
join SplitDirOrFileName and SplitDirOrFile
safareli Feb 13, 2018
5b600ab
overName instead of mapInside{Path,Name}
safareli Feb 13, 2018
dee127a
remove some unsafe leftover
safareli Feb 13, 2018
9ef9a9f
use published version of ps-strings
safareli Feb 13, 2018
c2f5cdd
We should only have DirPath in ParentIn
safareli Feb 13, 2018
ae145cd
clenup appendPath
safareli Feb 13, 2018
d85f0d2
Add computed sandbox outcomes
garyb Feb 13, 2018
26710de
add view functions and types
safareli Feb 14, 2018
a336f4b
Merge branch 'master' into refactor
safareli Feb 14, 2018
5f1fc17
fix shadowed dir
safareli Feb 14, 2018
6dad268
add relativify and absolutify
safareli Feb 15, 2018
166bb20
use one node for Current and Root
safareli Feb 16, 2018
6e69d23
use Tuple and List for View types
safareli Feb 16, 2018
40a3cb4
simplify relativify and absolutify
safareli Feb 16, 2018
0c58cf3
refactor view*; add view*Unsandboxed
safareli Feb 16, 2018
734c8c3
fix warning
safareli Feb 16, 2018
a95bcfb
Remove sandboxing
garyb Feb 19, 2018
c657b9b
Make `dirOrFile` and `absOrRel` folds
garyb Feb 19, 2018
573bbbc
Add `foldPath` function
garyb Feb 19, 2018
611bb79
Pass proof of type along with `relOrAbs` / `dirOrFile`
garyb Feb 19, 2018
821a76f
Rename `ParentIn` / `parentDir` to `parentOf`
garyb Feb 19, 2018
41a31d4
Sandboxing & safe printing
garyb Feb 19, 2018
9882545
Reorganisation and make `relativeTo` total
garyb Feb 20, 2018
cf8e92f
Fix some issues in `relativeTo` and restore sandboxing again
garyb Feb 21, 2018
8b31da9
Fix warnings
garyb Feb 21, 2018
62dfcc5
Use similar interface for printing and parsing
garyb Feb 21, 2018
36cb263
Reduce namespace to just `Pathy`
garyb Feb 21, 2018
60f97ce
Organize a bit for a main re-exports module
garyb Feb 21, 2018
43e11bc
Add warning when using totally unsafe path printing
garyb Feb 21, 2018
d757188
Only use proxies for name construction?
garyb Feb 21, 2018
cde2bfc
add comment to dir
safareli Feb 22, 2018
6dc7471
add typelevel-prelude to deps
safareli Feb 22, 2018
e9cd0d3
use new `{split,join}Name` for alterExtension and extention definitions
safareli Feb 22, 2018
2402cd2
add newlines in parser definition
safareli Feb 23, 2018
5d76c88
add testcase for /foo/././//bar/
safareli Feb 23, 2018
40864f0
optimise Array.reverse >>> List.fromFoldable
safareli Feb 23, 2018
a3655ca
add comment about `/../` case in canonicalize
safareli Feb 23, 2018
1de3862
add @(ParentOf _) to canonicalize
safareli Feb 23, 2018
080dbfa
add `peel' = canonicalize >>> peel`
safareli Feb 23, 2018
3abd056
use foldRelOrAbs instead of onRelOrAbs in printPath
safareli Feb 23, 2018
6780a87
Add property to relativeTo comment
safareli Feb 23, 2018
68e9764
simplify canonicalize ParentOf ParentOf case
safareli Feb 23, 2018
9d4b016
update comments remove peel'
safareli Feb 23, 2018
04466b6
fix windowsPrinter
safareli Feb 23, 2018
192c9db
Update readme examples, etc
garyb Feb 23, 2018
6388228
Add some tests for windows path printing
garyb Feb 23, 2018
fc39652
Only store canonical representations of paths
garyb Feb 24, 2018
ab1dc30
remove IsRelOrAbs from sandboxAny
safareli Feb 26, 2018
25a863d
add `AnyDir` and `AnyFile`
safareli Feb 27, 2018
b3f7359
fix warning
safareli Feb 27, 2018
032bf07
add in'
safareli Mar 9, 2018
bb1614e
reexport foldRelOrAbs, onRelOrAbs, foldDirOrFile, onDirOrFile from Pathy
safareli Mar 20, 2018
4b1e07f
add genDirName and genFileName
safareli Mar 20, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 41 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,14 @@

A type-safe abstraction for platform-independent file system paths.

# Example

```purescript
fullPath = rootDir </> dir "baz" </> file "foo.png"
```

See the [tests file](/test/Main.purs) for various example usages more.

# Getting Started

## Installation

```bash
bower install purescript-pathy
```

```purescript
import Data.Path.Pathy
import Pathy
```

## Introduction
Expand All @@ -34,9 +24,8 @@ Many path libraries provide a single abstraction to deal with file system paths.

* The distinction between relative and absolute paths.
* The distinction between paths denoting file resources and paths denoting directories.
* The distinction between paths that are secure (sandboxed to some location in the file system) and those that are insecure.

*Pathy* also uses a single abstraction for file system paths, called `Path`, but uses *phantom types* to keep track of the above distinctions.
Pathy also uses a single abstraction for file system paths, called `Path`, but uses *phantom types* to keep track of the above distinctions.

This approach lets you write code that performs type-safe composition of relative, absolute, file, and directory paths, and makes sure you never use paths in an unsafe fashion. Bogus and insecure operations simply aren't allowed by the type system!

Expand All @@ -46,48 +35,50 @@ Many paths come from user-input or configuration data. Pathy can parse such stri

Building path liberals is easy. You will typically build path literals from the following components:

* `rootDir` &mdash; The root directory of an absolute path.
* `currentDir` &mdash; The current directory (AKA the "working directory"), useful for building relative paths.
* `file` &mdash; A file (in the current directory).
* `dir` &mdash; A directory (in the current directory).
* `(</>)` &mdash; Adds a relative path to the end of a (relative or absolute) path.
* `(<.>)` &mdash; Sets the extension of a file path.
* `(<..>)` &mdash; Ascends one level in a directory, then descends into the specified relative path.
* `rootDir` – The root directory of an absolute path.
* `currentDir` – The current directory (AKA the "working directory"), useful for building relative paths.
* `file` – A file (in the current directory).
* `dir` – A directory (in the current directory).
* `(</>)` – Adds a relative path to the end of a (relative or absolute) path.
* `(<.>)` – Sets the extension of a file path.
* `(<..>)` – Ascends one level in a directory, then descends into the specified relative path.

All path segments (`file` / `dir`) names are required to be non-empty. This is enforced by `Name` being constructed from a `NonEmptyString`. At compile time, we can have provably non-empty strings by using `Symbol`s and a bit of type class trickery:

For example:
``` purescript
dirFoo :: Name Dir
dirFoo = dir (SProxy :: SProxy "foo")
```

Here we're using a symbol proxy (`SProxy`) and then typing it to explicitly carry the name that we want to use for our path at runtime. There is also a `dir'` and `file'` variation on the function that accepts normal `Name` values, so if you are not constructing a path at compile-time, you'd be using these instead.

Some example compile-time path constructions:

```purescript
let
path1 = rootDir </> dir "foo" </> dir "bar" </> file "baz.boo"
path2 = currentDir </> dir "foo"
in do
trace $ show $ printPath path1
trace $ show $ printPath path2
path1 = rootDir </> dir (SProxy :: SProxy "foo") </> dir (SProxy :: SProxy "bar") </> file (SProxy :: SProxy "baz.boo")
path2 = currentDir </> dir (SProxy :: SProxy "foo")
```

Pathy doesn't let you create combinators that don't make sense, such as:
Thanks to the phantom type parameters, Pathy doesn't let you create path combinations that don't make sense. The following examples will be rejected at compile time:

```purescript
rootDir </> rootDir
rootDir </> rootDir
currentDir </> rootDir
file "foo" </> file "bar"
file "foo" </> dir "bar"
file (SProxy :: SProxy "foo") </> file (SProxy :: SProxy "bar")
file (SProxy :: SProxy "foo") </> dir (SProxy :: SProxy "bar")
```

All these combinations will be disallowed at compile time!

### The Path Type

The `Path a b s` type has three type parameters:
The `Path a b` type has two type parameters:

* `a` &mdash; This may be `Abs` or `Rel`, indicating whether the path is absolute or relative.
* `b` &mdash; This may be `Dir` or `File`, indicating whether the path is a file or directory.
* `s` &mdash; This may be `Sandboxed` or `Unsandboxed`, indicating whether the path has been sandboxed yet or not.
* `a` – This may be `Abs` or `Rel`, indicating whether the path is absolute or relative.
* `b` – This may be `Dir` or `File`, indicating whether the path is a file or directory.

You should try to make the `Path` functions that you write as generic as possible. If you have a function that only cares if a path refers to a file, then you can write it like this:

```purescript
myFunction :: forall a s. Path a File s -> ...
myFunction :: forall a. Path a File -> ...
myFunction p = ...
```

Expand All @@ -97,38 +88,34 @@ By universally quantifying over the type parameters you don't care about, you en

To parse a string into a `Path`, you can use the `parsePath` function, which expects you to handle four cases:

* `Path Rel File Unsandboxed`
* `Path Abs File Unsandboxed`
* `Path Rel Dir Unsandboxed`
* `Path Abs Dir Unsandboxed`
* `Path Rel File`
* `Path Abs File`
* `Path Rel Dir`
* `Path Abs Dir`

If you need a specific case, you can use helper functions such as `parseRelFile`, which return a `Maybe`.

### Print Paths to Strings

You can print any path as a `String` by calling the `printPath` function.

For security reasons, you can only perform this operation if you have *sandboxed* the path. Sandboxing a path ensures that users cannot escape a sandbox directory that you specify; it's the right thing to do!
The `parsePath` function also expects a `Parser` argument so that different path formats can be parsed into the common `Path` type.

### Sandboxing

Pathy makes it easy to create relative paths, even paths that ascend into parent directories of relative paths.

With this power comes danger: if you parse a user string, the user may be able to escape any arbitrary directory.
Pathy makes it easy to create relative paths, even paths that ascend into parent directories of relative paths. With this power comes danger: if you parse a user string, the user may be able to escape any arbitrary directory.

Pathy solves this security problem by *disallowing* conversion from a `Path` to a `String` until the `Path` has been *sandboxed*.

To sandbox a path, you just call `sandbox` and provide the sandbox directory, as well as the path to sandbox:

```purescript
sandbox (rootDir </> dir "foo") (rootDir </> dir "foo" </> dir "bar")
sandbox
(rootDir </> dir (SProxy :: SProxy "foo")) -- sandbox root
(rootDir </> dir (SProxy :: SProxy "foo") </> dir (SProxy :: SProxy "bar")) -- path to sandbox
```

This returns a `Maybe`, which is either equal to `Nothing` if the tainted path escapes the sandbox, or `Just p`, where `p` is the tainted path, relative to the sandbox path.
This returns a `Maybe`, which is `Nothing` if the tainted path escapes the sandbox.

After you have sandboxed a foreign path, you may call `printPath` on it. There's no need to remember this rule because it's enforced at compile-time by phantom types!
After you have sandboxed a foreign path, you may call `printPath` on it, which will print the path absolutely.

All the path literals you build by hand are automatically sandboxed, unless you call `parentDir'` on them.
There is also the option to `unsafePrintPath`. This is labelled as being unsafe as it may be depending on how it is used - for example, if a path was sandboxed against some path other than the current working directory, but then used when launching a command in the current working directory, it may still refer to a location that it should not have access to.

### Renaming, Transforming, Etc.

Expand Down
8 changes: 4 additions & 4 deletions bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
"purescript-lists": "^4.0.0",
"purescript-partial": "^1.2.0",
"purescript-profunctor": "^3.0.0",
"purescript-strings": "^3.0.0",
"purescript-strings": "^3.5.0",
"purescript-transformers": "^3.0.0",
"purescript-unsafe-coerce": "^3.0.0"
"purescript-unsafe-coerce": "^3.0.0",
"purescript-typelevel-prelude": "^2.6.0"
},
"devDependencies": {
"purescript-quickcheck": "^4.0.0",
"purescript-quickcheck-laws": "^3.0.0"
"purescript-quickcheck": "^4.0.0"
}
}
Loading