You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

276 lines
9.0 KiB

/**
`Path` is a library for creating and operating on file paths consistently on
all platforms.
`Path` works exactly the same on Windows, Linux, and OSX, instead of adjusting
behavior based on your current OS
The `Path` API uses the following conventions:
- Accepts/returns only `t(absolute))` for values that must be absolute paths.
- Accepts/returns only `t(relative))` for values that must be absolute paths.
- Accepts `t('any)` for values that may be either absolute or relative paths.
- Returns `firstClass = Absolute(t(absolute)) | Relative(t(relative)` for
return values that could be either. Consumers must pattern match on it.
- Wraps return values in `Some(..)` / `None` when it is possible that no
value may be computed even when the caller supplies valid data.
- Wraps return values in `Ok(..)` / `Error(exn)` when it is possible that no
value may be computed due to an error occuring in either user input or system
failure.
- For every `functionName` that wraps return values in `Ok`/`Error`, an
alternative form `functionNameExn` is also supplied which does not wrap
the return value, and instead raises an exception.
TODO: Consider the following universal convention instead:
type specificUsageError = UserNameInvalid | LoggedOut;
type blame('usage) = | Caller('usage) | Implementation(exn);
// Returns Error only for system blame:
result(x, exn)
// Returns Error for caller/system blame
result(x, blame(usage))
// Returns Error only for caller blame
result(x, usage)
// Returns Error for caller/system blame, but no value expected.
result(option(x), blame(usage))
// Returns Error for system blame, but no value expected.
result(option(x), exn)
*/
type relative;
type absolute;
/**
A file system path, parameterized on the kind of file system path,
`Path.t(relative)` or `Path.t(absolute)`.
*/
type t('kind);
/**
Used to allow dynamically checking whether or not a path is absolute or
relative. Use seldomly.
*/
type firstClass =
| Absolute(t(absolute))
| Relative(t(relative));
let drive: string => t(absolute);
let root: t(absolute);
let home: t(relative);
let dot: t(relative);
/**
Queries whether a path is absolute or relative. Use seldomly, and typically
only on end-user input. Once queried, use the wrapped `t(absolute)/t(relative)`
as the primary path passed around.
*/
let testForPath: string => option(firstClass);
/**
Same as `testForPath`, but raises `Invalid_argument` if no path could be
detected.
*/
let testForPathExn: string => firstClass;
/**
Creates a "first class" path could be _either_ a relative path or an absolute
one.
This allows you to return values from functions that might be absolute or might
be relative. It also allows relative and absolute paths to coexist inside of a
list together.
For example, if you create a polymorphic function that accepts any kind of
path, and then you want to do something differently based on whether or not the
path is relative or absolute, you would first use `firstClass(path)` and then
pattern match on the result `Absolute(p) => .. | Relative(p) => ...`.
*/
let firstClass: t('any) => firstClass;
/**
Prints absolute `Path.t` as strings, always removes the final `/` separator.
*/
let toString: t(absolute) => string;
/**
Prints any `Path.t` for debugging, always removes the final `/` separator
except in the case of the empty relative paths `./`, `~/`.
*/
let toDebugString: t('kind) => string;
/**
Parses an absolute path into a `Path.t(absolute)` or returns `None` if the path
is not a absolute, yet still valid. Raises Invalid_argument if the path is
invalid.
*/
let absolute: string => option(t(absolute));
/**
Parses a relative path into a `Path.t(relative)` or returns `None` if the path
is not a valid.
*/
let relative: string => option(t(relative));
/**
Same as `Path.absolute` but raises a Invalid_argument if argument is not a
valid absolute path.
*/
let absoluteExn: string => t(absolute);
/**
Same as `Path.relative` but raises a Invalid_argument if argument is not a
valid relative path.
*/
let relativeExn: string => t(relative);
/**
Creates a relative path from two paths, which is the relative path that is
required to arive at the `dest`, if starting from `source` directory. The
`source` and `dest` must both be `t(absolute)` or `t(relative)`, but the
returned path is always of type `t(relative)`.
If `source` and `dest` are relative, it is assumed that the two relative paths
are relative to the same yet-to-be-specified absolute path.
relativize(~source=/a, ~dest=/a) == ./
relativize(~source=/a/b/c/d /a/b/qqq == ../c/d
relativize(~source=/a/b/c/d, ~dest=/f/f/zzz) == ../../../../f/f/zz
relativize(~source=/a/b/c/d, ~dest=/a/b/c/d/q) == ../q
relativize(~source=./x/y/z, ~dest=./a/b/c) == ../../a/b/c
relativize(~source=./x/y/z, ~dest=../a/b/c) == ../../../a/b/c
Unsupported:
`relativize` only accepts `source` and `dest` of the same kind of path because
the following are meaningless:
relativize(~source=/x/y/z, ~dest=./a/b/c) == ???
relativize(~source=./x/y/z, ~dest=/a/b/c) == ???
Exceptions:
If it is impossible to create a relative path from `soure` to `dest` an
exception is raised.
If `source`/`dest` are absolute paths, the drive must match or an exception is
thrown. If `source`/`dest` are relative paths, they both must be relative to
`"~"` vs. `"."`. If both are relative, but the source has more `..` than the
dest, then it is also impossible to create a relative path and an exception is
raised.
relativize(~source=./foo/bar, ~dest=~/foo/bar) == raise(Invalid_argument)
relativize(~source=~/foo/bar, ~dest=./foo/) == raise(Invalid_argument)
relativize(~source=C:/foo/bar, ~dest=/foo/bar) == raise(Invalid_argument)
relativize(~source=C:/foo/bar, ~dest=F:/foo/bar) == raise(Invalid_argument)
relativize(~source=/foo/bar, ~dest=C:/foo/) == raise(Invalid_argument)
relativize(~source=../x/y/z, ~dest=./a/b/c) == raise(Invalid_argument)
relativize(~source=../x/y/z, ~dest=../foo/../a/b/c) == raise(Invalid_argument)
*/
let relativizeExn: (~source: t('kind), ~dest: t('kind)) => t(relative);
/**
Same as `relativizeExn` but returns `result(Path.t(Path.absolute), exn)`
instead of throwing an exception.
*/
let relativize:
(~source: t('kind), ~dest: t('kind)) => result(t(relative), exn);
/**
Accepts any `Path.t` and returns a `Path.t` of the same kind. Relative path
inputs return relative path outputs, and absolute path inputs return absolute
path outputs.
*/
let dirName: t('kind) => t('kind);
/**
Accepts any `Path.t` and returns the final segment in its path string, or
`None` if there are no segments in its path string.
Path.baseName(Path.At(Path.dot /../ ""))
None
Path.baseName(Path.At(Path.dot /../ "foo"))
Some("foo")
Path.baseName(Path.At(Path.dot /../ "foo" /../ ""))
None
Path.baseName(Path.At(Path.dot /../ "foo" / "bar" /../ ""))
Some("foo")
*/
let baseName: t('kind) => option(string);
/**
Appends one segment to a path. Preserves the relative/absoluteness of the first
arguments.
*/
let append: (t('kind), string) => t('kind);
/**
Appends one path to another. Preserves the relative/absoluteness of the first
arguments.
*/
let join: (t('kind1), t('kind2)) => t('kind1);
let eq: (t('kind1), t('kind2)) => bool;
/**
Tests for path equality of two absolute paths.
*/
let absoluteEq: (t(absolute), t(absolute)) => bool;
/**
Tests for path equality of two absolute paths.
*/
let relativeEq: (t(relative), t(relative)) => bool;
/**
Tests whether or not an absolute path has a parent path. Absolute paths such as
"C:/" and "/" have no parent dir.
*/
let hasParentDir: t(absolute) => bool;
/**
Returns `true` if a path exists inside another path `~ofPath` or is equal to
`~ofPath`.
*/
let isDescendent: (~ofPath: t('kind), t('kind)) => bool;
/**
Syntactic forms for utilities provided above. These are included in a separate
module so that it can be opened safely without causing collisions with other
identifiers in scope such as "root"/"home".
Use like this:
Path.At(Path.root / "foo" / "bar");
Path.At(Path.dot /../ "bar");
*/
module At: {
/**
Performs `append` with infix syntax.
*/
let (/): (t('kind), string) => t('kind);
/**
`dir /../ s` is equivalent to `append(dirName(dir), s)`
*/
let (/../): (t('kind), string) => t('kind);
/**
`dir /../../ s` is equivalent to `append(dirName(dirName(dir)), s)`
*/
let (/../../): (t('kind), string) => t('kind);
/**
`dir /../../../ s` is equivalent to
`append(dirName(dirName(dirName(dir))), s)`
*/
let (/../../../): (t('kind), string) => t('kind);
/**
`dir /../../../../ s` is equivalent to
`append(dirName(dirName(dirName(dirName(dir)))), s)`
*/
let (/../../../../): (t('kind), string) => t('kind);
/**
`dir /../../../../../ s` is equivalent to
`append(dirName(dirName(dirName(dirName(dirName(dir))))), s)`
*/
let (/../../../../../): (t('kind), string) => t('kind);
/**
`dir /../../../../../../ s` is equivalent to
`append(dirName(dirName(dirName(dirName(dirName(dirName(dir)))))), s)`
*/
let (/../../../../../../): (t('kind), string) => t('kind);
};