Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ All Result types (both Ok and Err) implement these methods:
#### Value Extraction
- `unwrap(): mixed` - Returns the success value or throws UnwrapException (extends LogicException)
- `unwrapErr(): mixed` - Returns the error value or throws UnwrapException (extends LogicException)
- `expect(string $message): mixed` - Returns the success value or throws UnwrapException with the given message and a summary of the error value
- `expectErr(string $message): mixed` - Returns the error value or throws UnwrapException with the given message and a summary of the success value
- `unwrapOr(mixed $default): mixed` - Returns the success value or a default
- `unwrapOrElse(callable $fn): mixed` - Returns the success value or computes it from the error

Expand Down
15 changes: 15 additions & 0 deletions src/Err.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,21 @@ public function unwrapErr(): mixed
return $this->value;
}

#[Override]
public function expect(string $message): never
{
throw UnwrapException::withMessage($message, $this->value);
}

/**
* @return E
*/
#[Override]
public function expectErr(string $message): mixed
{
return $this->value;
}

/**
* @template U
* @param U $default
Expand Down
15 changes: 15 additions & 0 deletions src/Ok.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,21 @@ public function unwrapErr(): never
throw UnwrapException::unwrapErrOnOk($this->value);
}

/**
* @return T
*/
#[Override]
public function expect(string $message): mixed
{
return $this->value;
}

#[Override]
public function expectErr(string $message): never
{
throw UnwrapException::withMessage($message, $this->value);
}

/**
* @template U
* @param U $default
Expand Down
22 changes: 22 additions & 0 deletions src/Result.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,28 @@ public function unwrap(): mixed;
*/
public function unwrapErr(): mixed;

/**
* 成功値を返します。失敗の場合は指定したメッセージで例外を投げます.
*
* @param string $message 失敗時の例外メッセージ(エラー値の要約が付加されます)
*
* @return ($this is Ok<mixed> ? T : never)
*
* @throws UnwrapException $this が Err の場合
*/
public function expect(string $message): mixed;

/**
* エラー値を返します。成功の場合は指定したメッセージで例外を投げます.
*
* @param string $message 成功時の例外メッセージ(成功値の要約が付加されます)
*
* @return ($this is Err<mixed> ? E : never)
*
* @throws UnwrapException $this が Ok の場合
*/
public function expectErr(string $message): mixed;

/**
* 成功値またはデフォルト値を返します.
*
Expand Down
8 changes: 8 additions & 0 deletions src/UnwrapException.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ public static function unwrapErrOnOk(mixed $value): self
return new self(\sprintf('called Result::unwrapErr() on an Ok value: %s', self::describe($value)));
}

/**
* expect() / expectErr() 用に、呼び出し側のメッセージと値の要約から例外を生成します.
*/
public static function withMessage(string $message, mixed $value): self
{
return new self(\sprintf('%s: %s', $message, self::describe($value)));
}

/**
* 例外メッセージ用に値の要約を生成します.
*
Expand Down
25 changes: 25 additions & 0 deletions tests/ErrTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,22 @@ public function unwrap_throws_exception(): void
$err->unwrap();
}

#[Test]
public function expect_throws_withGivenMessage(): void
{
$err = new Err('error');
$this->expectException(\LogicException::class);
$this->expectExceptionMessage('config file should be readable');
$err->expect('config file should be readable');
}
Comment on lines +62 to +69

#[Test]
public function expectErr_returns_error_value(): void
{
$err = new Err('error');
$this->assertSame('error', $err->expectErr('should have an error'));
}

Comment on lines +62 to +77

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

コンビネータ・ネストとの組み合わせテストの追加を検討

expect/expectErr 単体の成功・例外系は網羅されていますが、map/andThen/orElse などのコンビネータと組み合わせた場合や、ネストした Result に対する expect/expectErr の挙動を検証するテストが見当たりません。既存の unwrap/unwrapErr にも同種のテストは無いため必須ではありませんが、Rust の Result API に倣うなら組み合わせ利用が想定されるため、余裕があれば追加を検討してください。

As per path instructions, テストは「エッジケース(Ok/Err 双方、ネスト、map/and_then などのコンビネータ)が網羅されているかを見る」必要があります。

Also applies to: 165-173

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/ErrTest.php` around lines 62 - 77, The ErrTest coverage is missing
combined scenarios for expect/expectErr with Result combinators and nested
Result values. Add tests around Err::expect and Err::expectErr that exercise
map, andThen, and orElse interactions, plus nested Ok/Err cases, so the behavior
is verified beyond the current standalone success and exception tests.

Source: Path instructions

#[Test]
public function unwrap_throwsUnwrapException_withErrorValueInMessage(): void
{
Expand Down Expand Up @@ -146,6 +162,15 @@ public function unwrap_withMultilineStringError_keepsMessageSingleLine(): void
}
}

#[Test]
public function expect_throwsUnwrapException_withErrorValueInMessage(): void
{
$err = new Err(new \RuntimeException('boom'));
$this->expectException(UnwrapException::class);
$this->expectExceptionMessage('config file should be readable: RuntimeException: boom');
$err->expect('config file should be readable');
}

#[Test]
public function unwrapErr_returns_error_value(): void
{
Expand Down
25 changes: 25 additions & 0 deletions tests/OkTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,22 @@ public function unwrapErr_throws_exception(): void
$ok->unwrapErr();
}

#[Test]
public function expect_returns_value(): void
{
$ok = new Ok(42);
$this->assertSame(42, $ok->expect('should have a value'));
}

#[Test]
public function expectErr_throws_withGivenMessage(): void
{
$ok = new Ok(42);
$this->expectException(\LogicException::class);
$this->expectExceptionMessage('should have an error');
$ok->expectErr('should have an error');
}
Comment on lines +100 to +107

#[Test]
public function unwrapErr_throwsUnwrapException_withValueInMessage(): void
{
Expand All @@ -114,6 +130,15 @@ public function __toString(): string
$ok->unwrapErr();
}

#[Test]
public function expectErr_throwsUnwrapException_withValueInMessage(): void
{
$ok = new Ok(42);
$this->expectException(UnwrapException::class);
$this->expectExceptionMessage('should have an error: 42');
$ok->expectErr('should have an error');
}

#[Test]
public function unwrapOr_returns_value(): void
{
Expand Down
11 changes: 11 additions & 0 deletions tests/Types/result.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,17 @@ function testUnwrapOnGenericReceiver(Result $result): void
assertType('RuntimeException', $result->unwrapErr());
}

/**
* expect / expectErr も unwrap / unwrapErr と同じ条件付き戻り値型が解決される.
*
* @param Result<int, RuntimeException> $result
*/
function testExpectOnGenericReceiver(Result $result): void
{
assertType('int', $result->expect('should have a value'));
assertType('RuntimeException', $result->expectErr('should have an error'));
}

/**
* 具象レシーバでの unwrapOr / unwrapOrElse: 実行時に起こり得ない側の型を混ぜない.
*
Expand Down
Loading