Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
d13e040
require php 8.4
Baptouuuu Nov 9, 2025
148d063
remove remnants of phpunit
Baptouuuu Nov 9, 2025
4d4143e
update dependencies
Baptouuuu Nov 9, 2025
bd41d17
remove dependency to symfony
Baptouuuu Nov 9, 2025
65fa4e8
silently ignore links when reading from filesystem
Baptouuuu Nov 9, 2025
a821bfb
verify path length before trying to remove it
Baptouuuu Nov 9, 2025
2f8f1d2
remove unused constant
Baptouuuu Nov 9, 2025
2e481ef
Filesystem::mount() now returns an Attempt instead of throwing
Baptouuuu Nov 9, 2025
8615a1a
remove deprecated InMemory::new()
Baptouuuu Nov 9, 2025
1e51a7b
use promoted properties
Baptouuuu Nov 9, 2025
d25f0b7
use exclude method instead of negating condition
Baptouuuu Nov 9, 2025
da97e8a
discard errors when checking if a file exists
Baptouuuu Nov 9, 2025
52f12f8
flag Directory::removed() as internal
Baptouuuu Nov 9, 2025
30dae56
force specifying the case sensitivity at mount time
Baptouuuu Nov 10, 2025
0942c01
better integrate case sensitivity tests in blackbox
Baptouuuu Nov 10, 2025
fa23c1f
add representation of relative tree path to translate it to a concret…
Baptouuuu Nov 10, 2025
b371e07
fix path containing a double /
Baptouuuu Nov 10, 2025
ca5d091
compute the absolute path to remove as late as possible
Baptouuuu Nov 10, 2025
be44a52
add bridge while transitionning from old interface to a final class
Baptouuuu Nov 10, 2025
0d2ad08
add the next internal interface
Baptouuuu Nov 10, 2025
80bdaf6
add Implementation::exists()
Baptouuuu Nov 10, 2025
64dce4c
remove the need for Filesystem to implement Adapter
Baptouuuu Nov 10, 2025
5635e1d
add method to read from a path
Baptouuuu Nov 10, 2025
414cec5
add method to list tree paths
Baptouuuu Nov 10, 2025
e804a25
remove dead code
Baptouuuu Nov 10, 2025
34c5744
remove duplicated logic to recursively traverse directories
Baptouuuu Nov 10, 2025
44455da
make sure the implementations only return relative names and not the …
Baptouuuu Nov 10, 2025
1a7fa3b
remove dead code
Baptouuuu Nov 10, 2025
9302eca
remove dead code
Baptouuuu Nov 10, 2025
8683979
add Implementation::remove()
Baptouuuu Nov 10, 2025
8744688
simplify reading from the filesystem
Baptouuuu Nov 11, 2025
1c2749f
let the bridge handle the logic to create/delete files/directories
Baptouuuu Nov 11, 2025
611a7e0
handle case sensitivity in the bridge
Baptouuuu Nov 11, 2025
447eade
prevent writing unchanged file/directory
Baptouuuu Nov 11, 2025
f681820
remove dead code
Baptouuuu Nov 11, 2025
fadb579
change implementation type to prevent reaching an impossible case
Baptouuuu Nov 11, 2025
42236ce
use null when media type is not parseable to let use the default one
Baptouuuu Nov 11, 2025
192cc67
encapsulate the knownledge if the path to read is a file/directory or…
Baptouuuu Nov 11, 2025
3c09017
let the bridge work with any implementation
Baptouuuu Nov 11, 2025
78819db
give a more easy access to the file name being written
Baptouuuu Nov 11, 2025
5cde856
give a more easy access to the directory name being created
Baptouuuu Nov 11, 2025
31803a4
give a more easy access to the file/directory name to remove
Baptouuuu Nov 11, 2025
a3465b9
make sure to always list with a tree path representing a directory
Baptouuuu Nov 11, 2025
e4a4213
make InMemory implement Implementation
Baptouuuu Nov 11, 2025
f7c9f4d
make Adapter a final class
Baptouuuu Nov 11, 2025
6739d8c
CS
Baptouuuu Nov 11, 2025
4637c7d
Merge branch 'adapter-as-final-class' into next
Baptouuuu Nov 11, 2025
3a74e90
bump blackbox
Baptouuuu Nov 11, 2025
f297bd4
remove wip
Baptouuuu Nov 11, 2025
6b6fa9f
remove custom exceptions
Baptouuuu Nov 11, 2025
c9aa41b
add missing return type
Baptouuuu Nov 18, 2025
2920298
add mechanism to let the caller decide if the mounted directory shoul…
Baptouuuu Nov 19, 2025
c36151c
remove warnings
Baptouuuu Dec 13, 2025
6216fcc
use IO to read file media type
Baptouuuu Dec 13, 2025
f615ed9
use IO to list files
Baptouuuu Dec 13, 2025
ceea075
use IO to check if files/directories exist
Baptouuuu Dec 13, 2025
3617138
fix removing files starting with the same name as the one added
Baptouuuu Dec 13, 2025
a06325f
use IO to create files/directories
Baptouuuu Dec 14, 2025
14801f9
remove duplication of path length verification
Baptouuuu Dec 14, 2025
0318886
use IO to remove files
Baptouuuu Dec 14, 2025
5609332
avoid validating the same path multiple times
Baptouuuu Dec 14, 2025
a4e3403
improve the way to access different kinds of files
Baptouuuu Dec 14, 2025
862db59
remove Content::atPath()
Baptouuuu Dec 14, 2025
394f8e3
rely on the kind of file to know what to do
Baptouuuu Dec 14, 2025
20b78b6
let the read function handles links listed in a directory
Baptouuuu Dec 14, 2025
8bf8d4d
fail when trying to remove a link
Baptouuuu Dec 14, 2025
5feb57f
add missing dependency
Baptouuuu Dec 14, 2025
c5fc22e
add extensive ci
Baptouuuu Dec 15, 2025
3ad26a6
run properties on a simulated disk
Baptouuuu Dec 15, 2025
9c9ef47
test case insensitive simulated disks
Baptouuuu Dec 17, 2025
026ec1b
rename read into access for consistency with innmind/io
Baptouuuu Dec 17, 2025
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
10 changes: 4 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ on: [push]

jobs:
blackbox:
uses: innmind/github-workflows/.github/workflows/black-box-matrix.yml@main
uses: innmind/github-workflows/.github/workflows/black-box-matrix.yml@next
with:
scenarii: 20
coverage:
uses: innmind/github-workflows/.github/workflows/coverage-matrix.yml@main
uses: innmind/github-workflows/.github/workflows/coverage-matrix.yml@next
secrets: inherit
psalm:
uses: innmind/github-workflows/.github/workflows/psalm-matrix.yml@main
uses: innmind/github-workflows/.github/workflows/psalm-matrix.yml@next
cs:
uses: innmind/github-workflows/.github/workflows/cs.yml@main
with:
php-version: '8.2'
uses: innmind/github-workflows/.github/workflows/cs.yml@next
12 changes: 12 additions & 0 deletions .github/workflows/extensive.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Extensive CI

on:
push:
tags:
- '*'
paths:
- '.github/workflows/extensive.yml'

jobs:
blackbox:
uses: innmind/github-workflows/.github/workflows/extensive.yml@main
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
composer.lock
vendor
.phpunit.result.cache
.cache
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
# Changelog

## [Unreleased]

### Added

- `Innmind\Filesystem\Recover`

### Changed

- Requires PHP `8.4`
- Links are not silently ignored when listing files/directories. An error is still returned when trying to remove a link.
- `Innmind\Filesystem\Adapter\Filesystem::mount()` now returns an `Innmind\Immutable\Attempt`
- `Innmind\Filesystem\Directory::removed()` is now flagged as internal
- `Innmind\Filesystem\Adapter` is now a final class
- `Innmind\Filesystem\Adapter\Filesystem` is now flagged as internal
- `Innmind\Filesystem\Adapter\InMemory` is now flagged as internal
- `Innmind\Filesystem\Adapter\Logger` is now flagged as internal
- `Innmind\Filesystem\Adapter::mount()` no longer automatically create the directory if it doesn't exist

### Removed

- `Innmind\Filesystem\Adapter\InMemory::new()`
- `Innmind\Filesystem\Adapter\Filesystem::withCaseSensitivity()`, case sensitivity can be specified as the second argument of `::mount()`
- `Innmind\Filesystem\Exception\*`
- `Innmind\Filesystem\File\Content::atPath()`, use `Content::io()` instead

## 8.1.0 - 2025-05-09

### Added
Expand Down
5 changes: 1 addition & 4 deletions blackbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@
)
->scenariiPerProof(1),
)
->when(
\method_exists(Application::class, 'allowProofsToNotMakeAnyAssertions'),
static fn($app) => $app->allowProofsToNotMakeAnyAssertions(),
)
->allowProofsToNotMakeAnyAssertions()
->tryToProve(Load::everythingIn(__DIR__.'/proofs/'))
->exit();
19 changes: 11 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@
"issues": "http://github.com/Innmind/filesystem/issues"
},
"require": {
"php": "~8.2",
"innmind/immutable": "~4.15|~5.0",
"symfony/filesystem": "~6.0|~7.0",
"innmind/media-type": "~2.1",
"innmind/url": "~4.2",
"php": "~8.4",
"innmind/immutable": "dev-next",
"innmind/media-type": "dev-next",
"innmind/url": "dev-next",
"psr/log": "~3.0",
"innmind/io": "^3.0.1",
"innmind/validation": "~2.0"
"innmind/io": "dev-next",
"innmind/validation": "dev-next",
"innmind/time-continuum": "dev-next",
"innmind/ip": "dev-next",
"innmind/mutable": "dev-next"
},
"autoload": {
"psr-4": {
Expand All @@ -38,8 +40,9 @@
},
"require-dev": {
"innmind/static-analysis": "^1.2.1",
"innmind/black-box": "^6.0.2",
"innmind/black-box": "~6.5",
"innmind/coding-standard": "~2.0",
"symfony/filesystem": "~6.0|~7.0",
"ramsey/uuid": "^4.6"
},
"conflict": {
Expand Down
26 changes: 0 additions & 26 deletions phpunit.xml.dist

This file was deleted.

94 changes: 71 additions & 23 deletions proofs/adapter/filesystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,67 +2,115 @@
declare(strict_types = 1);

use Innmind\Filesystem\{
Adapter\Filesystem,
Adapter,
Directory,
File,
File\Content,
CaseSensitivity,
Recover,
};
use Innmind\IO\{
IO,
Simulation\Disk,
};
use Innmind\Url\Path;
use Properties\Innmind\Filesystem\Adapter;
use Properties\Innmind\Filesystem\Adapter as PAdapter;
use Innmind\BlackBox\Set;
use Symfony\Component\Filesystem\Filesystem as FS;

return static function() {
$path = \rtrim(\sys_get_temp_dir(), '/').'/innmind/filesystem/';

yield properties(
'Filesystem properties',
Adapter::properties(),
Set::call(static function() {
$path = \sys_get_temp_dir().'/innmind/filesystem/';
PAdapter::properties(),
Set::call(static function() use ($path) {
(new FS)->remove($path);

return Filesystem::mount(Path::of($path))->withCaseSensitivity(match (\PHP_OS) {
'Darwin' => CaseSensitivity::insensitive,
default => CaseSensitivity::sensitive,
});
return Adapter::mount(
Path::of($path),
match (\PHP_OS) {
'Darwin' => CaseSensitivity::insensitive,
default => CaseSensitivity::sensitive,
},
)
->recover(Recover::mount(...))
->unwrap();
}),
);

foreach (Adapter::alwaysApplicable() as $property) {
foreach (PAdapter::alwaysApplicable() as $property) {
yield property(
$property,
Set::call(static function() {
$path = \sys_get_temp_dir().'/innmind/filesystem/';
Set::call(static function() use ($path) {
(new FS)->remove($path);

return Filesystem::mount(Path::of($path))->withCaseSensitivity(match (\PHP_OS) {
'Darwin' => CaseSensitivity::insensitive,
default => CaseSensitivity::sensitive,
});
return Adapter::mount(
Path::of($path),
match (\PHP_OS) {
'Darwin' => CaseSensitivity::insensitive,
default => CaseSensitivity::sensitive,
},
)
->recover(Recover::mount(...))
->unwrap();
}),
)->named('Filesystem');
}

yield test(
'Regression adding file in directory due to case sensitivity',
static function($assert) {
$property = new Adapter\AddRemoveAddModificationsStillAddTheFile(
static function($assert) use ($path) {
$property = new PAdapter\AddRemoveAddModificationsStillAddTheFile(
Directory::named('0')
->add($file = File::named('L', Content::none()))
->remove($file->name()),
File::named('l', Content::none()),
);

$path = \sys_get_temp_dir().'/innmind/filesystem/';
(new FS)->remove($path);
$adapter = Filesystem::mount(Path::of($path))->withCaseSensitivity(match (\PHP_OS) {
'Darwin' => CaseSensitivity::insensitive,
default => CaseSensitivity::sensitive,
});
$adapter = Adapter::mount(
Path::of($path),
match (\PHP_OS) {
'Darwin' => CaseSensitivity::insensitive,
default => CaseSensitivity::sensitive,
},
)
->recover(Recover::mount(...))
->unwrap();

$property->ensureHeldBy($assert, $adapter);

(new FS)->remove($path);
},
);

foreach (CaseSensitivity::cases() as $case) {
yield properties(
'Filesystem properties on simulated disk',
PAdapter::properties(),
Set::call(static fn() => Adapter::mount(
Path::of('/'),
$case,
IO::simulation(
IO::fromAmbientAuthority(),
Disk::new(),
),
)->unwrap()),
);

foreach (PAdapter::alwaysApplicable() as $property) {
yield property(
$property,
Set::call(static fn() => Adapter::mount(
Path::of('/'),
$case,
IO::simulation(
IO::fromAmbientAuthority(),
Disk::new(),
),
)->unwrap()),
)->named('Filesystem on simulated disk');
}
}
};
46 changes: 31 additions & 15 deletions proofs/adapter/inMemory.php
Original file line number Diff line number Diff line change
@@ -1,30 +1,46 @@
<?php
declare(strict_types = 1);

use Innmind\Filesystem\Adapter\InMemory;
use Properties\Innmind\Filesystem\Adapter;
use Innmind\Filesystem\{
Adapter,
Name,
File,
File\Content,
};
use Properties\Innmind\Filesystem\Adapter as PAdapter;
use Innmind\BlackBox\Set;

return static function() {
yield properties(
'InMemory properties',
Adapter::properties(),
Set::call(InMemory::new(...)),
);
yield properties(
'InMemory properties emulating filesystem',
Adapter::properties(),
Set::call(InMemory::emulateFilesystem(...)),
PAdapter::properties(),
Set::call(Adapter::inMemory(...)),
);

foreach (Adapter::alwaysApplicable() as $property) {
yield property(
$property,
Set::call(InMemory::new(...)),
)->named('InMemory');
foreach (PAdapter::alwaysApplicable() as $property) {
yield property(
$property,
Set::call(InMemory::emulateFilesystem(...)),
Set::call(Adapter::inMemory(...)),
)->named('InMemory emulating filesystem');
}

yield test(
'Adding a file in a directory should not remove other files starting with the same name',
static function($assert) {
$adapter = Adapter::inMemory();
$property = new PAdapter\AddDirectoryFromAnotherAdapterWithFileAdded(
Name::of('0'),
File::named(
'+1',
Content::none(),
),
File::named(
'+',
Content::none(),
),
);

$property->ensureHeldBy($assert, $adapter);
},
);
};
19 changes: 6 additions & 13 deletions proofs/file/content.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,6 @@
static fn($lines) => Model::ofString(\implode("\n", $lines)),
),
],
[
'Content::atPath()',
Set::of('LICENSE', 'CHANGELOG.md', 'composer.json')
->map(Path::of(...))
->map(static fn($path) => Model::atPath(
$io,
$path,
)),
],
[
'Content::io()',
Set::of('LICENSE', 'CHANGELOG.md', 'composer.json')
Expand Down Expand Up @@ -262,10 +253,12 @@ static function($assert, $a, $b) use ($io) {
yield test(
'Content::ofChunks()->size() does not load the whole file in memory',
static function($assert) use ($io) {
$atPath = Model::atPath(
$io,
Path::of('samples/sample.pdf'),
);
$atPath = $io
->files()
->access(Path::of('samples/sample.pdf'))
->map(static fn($file) => $file->read())
->map(Model::io(...))
->unwrap();
$content = Model::ofChunks($atPath->chunks());

$assert
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public function applicableTo(object $adapter): bool

public function ensureHeldBy(Assert $assert, object $adapter): object
{
$adapter->remove($this->file->name())->unwrap();
$_ = $adapter->remove($this->file->name())->unwrap();
$assert
->object($adapter->add($this->directory)->unwrap())
->instance(SideEffect::class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function applicableTo(object $adapter): bool

public function ensureHeldBy(Assert $assert, object $adapter): object
{
$adapter
$_ = $adapter
->add(
$this
->directory
Expand Down
Loading