dennisdoomen / dennisdoomen/pathy

[API Proposal]: IsUnder and Contains for safe ancestor checks

Open
#127 0 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
C#
Stars
27
Forks
5
Avg merge
21h 29m
Merged PRs (30d)
10

Description

### Background and motivation

Checking whether one path lives underneath another is one of the most common path operations, and one of the easiest to get wrong. The naive `a.ToString().StartsWith(b.ToString())` approach reports that `c:\foobar` is under `c:\foo`, and it also breaks on trailing separators and on mixed casing. Pathy already normalises separators and knows about rooting, so it is the right place to offer a correct implementation.

### API Proposal

```C#
namespace Pathy
{
public readonly struct ChainablePath
{
public bool IsUnder(ChainablePath other);
public bool Contains(ChainablePath other);
}
}
```

`IsUnder` returns `true` when this path is a strict descendant of `other`. `Contains` is the inverse, so it reads naturally from the directory side.

### API Usage

```C#
var repo = ChainablePath.From("c:/work/repo");

(repo / "src" / "Program.cs").IsUnder(repo); // true
(ChainablePath.From("c:/work/repository")).IsUnder(repo); // false, not just a string prefix
repo.Contains(repo / "src"); // true
repo.IsUnder(repo); // false, a path is not under itself
```

A typical use is guarding against path traversal after combining an untrusted segment:

```C#
var target = uploadRoot / userSuppliedName;
if (!target.IsUnder(uploadRoot))
{
throw new SecurityException("Path escapes the upload root.");
}
```

### Alternative Designs

* Only offer `IsUnder` and let callers flip the operands. `Contains` is cheap to add and makes guard clauses read better.
* Add an `includeSelf` parameter instead of fixing the strict-descendant semantics. A separate `Equals` check is clearer.

### Risks

The comparison needs a documented decision on case sensitivity. Matching the behaviour of the current `Equals` implementation keeps this consistent. Both paths should be normalised (and ideally made absolute) before comparison, otherwise the result depends on the current working directory.

Contributor guide

Open the contributing guide

Research direction

Start by locating ChainablePath and its current Equals implementation, then review how Pathy normalises separators and handles rooted paths. Confirm the case-sensitivity and absolute-path decisions against Equals. Done means IsUnder provides strict descendant checks, Contains is its inverse, and the documented examples distinguish path boundaries correctly.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.