diff --git a/src/Support/AtUri.php b/src/Support/AtUri.php index c7d6b53b..88e6eab7 100644 --- a/src/Support/AtUri.php +++ b/src/Support/AtUri.php @@ -5,11 +5,12 @@ use Illuminate\Support\Str; use InvalidArgumentException; use Revolution\AtProto\Lexicon\Attributes\Format; +use Stringable; /** * @link https://github.com/bluesky-social/atproto/blob/main/packages/syntax/src/aturi.ts */ -final readonly class AtUri +final readonly class AtUri implements Stringable { protected const ATP = 'at://'; @@ -18,7 +19,12 @@ protected ?string $protocol; protected ?string $host; protected ?string $pathname; + protected ?string $searchParams; + protected ?string $hash; + /** + * @throw InvalidArgumentException + */ public function __construct(protected string $uri) { if (! Str::startsWith($this->uri, self::ATP)) { @@ -32,13 +38,38 @@ public function __construct(protected string $uri) $this->protocol = $matches[1] ?? null; $this->host = $matches[2] ?? null; $this->pathname = $matches[3] ?? null; + $this->searchParams = $matches[4] ?? null; + $this->hash = $matches[5] ?? null; } + /** + * ``` + * $at = AtUri::parse('at://did:plc:+++/app.bsky.feed.post/+++'); + * $at->repo(); + * $at->collection(); + * $at->rkey(); + * ``` + * + * @throw InvalidArgumentException + */ public static function parse(#[Format('at-uri')] string $uri): self { return new self($uri); } + /** + * ``` + * $at = AtUri::make(repo: 'did:plc:+++', collection: 'app.bsky.feed.post', rkey: '+++'); + * $uri = $at->__toString();// 'at://did:plc:+++/app.bsky.feed.post/+++' + * ``` + * + * @throw InvalidArgumentException + */ + public static function make(string $repo, ?string $collection = null, ?string $rkey = null): self + { + return new self(self::ATP.collect(func_get_args())->implode('/')); + } + public function protocol(): string { return $this->protocol ?? ''; @@ -67,4 +98,16 @@ public function rkey(): string { return Str::of($this->pathname)->explode('/')->get(2, default: ''); } + + /** + * ``` + * $at = AtUri::parse('at://did:plc:+++/app.bsky.feed.post/+++'); + * $uri = (string) $at; + * $uri = $at->__toString(); + * ``` + */ + public function __toString(): string + { + return self::ATP.$this->host.$this->pathname.$this->searchParams.$this->hash; + } } diff --git a/tests/Feature/Support/SupportTest.php b/tests/Feature/Support/SupportTest.php index 9f47bf2c..7644a9b7 100644 --- a/tests/Feature/Support/SupportTest.php +++ b/tests/Feature/Support/SupportTest.php @@ -3,6 +3,7 @@ namespace Tests\Feature\Support; use Illuminate\Support\Facades\Http; +use InvalidArgumentException; use Revolution\Bluesky\Facades\Bluesky; use Revolution\Bluesky\Support\AtUri; use Revolution\Bluesky\Support\DID; @@ -91,6 +92,32 @@ public function test_at_uri() $this->assertSame('abcde', $at->rkey()); } + public function test_at_uri_invalid() + { + $this->expectException(InvalidArgumentException::class); + + $at = AtUri::parse('http://did:plc:test/app.bsky.feed.post/abcde'); + } + + public function test_at_uri_to_string() + { + $at = AtUri::parse('at://did:plc:test/app.bsky.feed.post/abcde?test=a#hash'); + + $this->assertSame('at://did:plc:test/app.bsky.feed.post/abcde?test=a#hash', (string) $at); + $this->assertSame('at://did:plc:test/app.bsky.feed.post/abcde?test=a#hash', $at->__toString()); + } + + public function test_at_uri_make() + { + $at = AtUri::make(repo: 'did:plc:test', collection: 'app.bsky.feed.post', rkey: 'abcde'); + $at2 = AtUri::make(repo: 'did:plc:test', collection: 'app.bsky.feed.post'); + $at3 = AtUri::make(repo: 'did:plc:test'); + + $this->assertSame('at://did:plc:test/app.bsky.feed.post/abcde', (string) $at); + $this->assertSame('at://did:plc:test/app.bsky.feed.post', $at2->__toString()); + $this->assertSame('at://did:plc:test', $at3->__toString()); + } + public function test_did_web() { $web = DID::web();