This repository has been archived by the owner on Dec 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from TBD54566975/parse
implement `Dap.parse`
- Loading branch information
Showing
8 changed files
with
217 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
on: | ||
push: | ||
branches: [main] | ||
pull_request: | ||
branches: [main] | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Init Hermit | ||
uses: cashapp/activate-hermit@v1 | ||
with: | ||
cache: true | ||
|
||
- name: Install Dependencies | ||
run: just deps | ||
|
||
- name: Run Static Analysis | ||
run: just analyze | ||
|
||
- name: Run Tests | ||
run: just test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
_help: | ||
@just -l | ||
|
||
deps: | ||
#!/bin/bash | ||
set -euo pipefail | ||
|
||
dart pub get | ||
|
||
test: | ||
#!/bin/bash | ||
set -euo pipefail | ||
|
||
dart test | ||
|
||
analyze: | ||
#!/bin/bash | ||
set -euo pipefail | ||
|
||
dart analyze |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
hermit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.just-1.29.1.pkg |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
import 'package:dap/dap.dart'; | ||
|
||
void main() { | ||
final dap = Dap(); | ||
print(dap.hehe); | ||
final dap = Dap.parse('@moegrammer/didpay.me'); | ||
print(dap.handle); // moegrammer | ||
print(dap.domain); // didpay.me | ||
print(dap.registryDid); // did:web:didpay.me | ||
print(dap); // @moegrammer/didpay.me | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,96 @@ | ||
/// Checks if you are awesome. Spoiler: you are. | ||
/// Represents a Decentralized Agnostic Paytag (DAP). | ||
class Dap { | ||
bool get hehe => true; | ||
String handle; | ||
String domain; | ||
String get registryDid => 'did:web:$domain'; | ||
String get dap => '@$handle/$domain'; | ||
|
||
Dap({required this.handle, required this.domain}); | ||
|
||
/// Factory function to create a Dap instance from a formatted string. | ||
/// | ||
/// The input string must be in the format `@handle/domain`, where: | ||
/// - `handle` is a user handle that starts with `@`. | ||
/// - `domain` is the domain associated with the handle. | ||
/// | ||
/// Throws a [FormatException] if the input string does not meet the required format | ||
/// or contains invalid characters. | ||
factory Dap.parse(String input) { | ||
if (input.isEmpty || input[0] != '@') { | ||
throw FormatException('Invalid DAP: Must start with "@"'); | ||
} | ||
|
||
int delimIdx = input.lastIndexOf('/'); | ||
if (delimIdx == -1) { | ||
throw FormatException('Invalid DAP: Must contain "/"'); | ||
} | ||
|
||
String handle = input.substring(1, delimIdx); | ||
String domain = input.substring(delimIdx + 1); | ||
|
||
if (domain.isEmpty) { | ||
throw FormatException('Invalid DAP: Domain cannot be empty'); | ||
} | ||
|
||
if (handle.length < 3 || handle.length > 30) { | ||
throw FormatException( | ||
'Invalid DAP: Handle must be between 3-30 characters'); | ||
} | ||
|
||
for (int i = 0; i < handle.length; i++) { | ||
var c = handle.codeUnitAt(i); | ||
if (_isControlCharacter(c) || _isPunctuation(c)) { | ||
throw FormatException('Invalid character in handle at pos $i'); | ||
} | ||
} | ||
|
||
return Dap(handle: handle, domain: domain); | ||
} | ||
|
||
@override | ||
String toString() { | ||
return dap; | ||
} | ||
|
||
/// Helper function to check if a character is a control character. | ||
/// | ||
/// Control characters are non-printable characters used to control the interpretation | ||
/// or display of text. In Unicode, control characters include: | ||
/// | ||
/// - ASCII control characters (U+0000 to U+001F) | ||
/// - Delete (U+007F) | ||
/// - C1 control characters (U+0080 to U+009F) | ||
/// | ||
/// References: | ||
/// - [Basic Latin Control Characters (C0 Controls)](https://www.unicode.org/charts/PDF/U0000.pdf) | ||
/// - [C1 Control Characters and Latin-1 Supplement](https://www.unicode.org/charts/PDF/U0080.pdf) | ||
static bool _isControlCharacter(int codeUnit) { | ||
return codeUnit <= 31 || (codeUnit >= 127 && codeUnit <= 159); | ||
} | ||
|
||
/// Helper function to check if a character is punctuation. | ||
/// | ||
/// Punctuation characters include various symbols that are used to separate sentences | ||
/// and phrases, but are not letters or digits. This function checks for characters | ||
/// in the following ranges: | ||
/// | ||
/// - `!` to `/` (U+0021 to U+002F) | ||
/// - `:` to `@` (U+003A to U+0040) | ||
/// - `[` to `` ` `` (U+005B to U+0060) | ||
/// - `{` to `~` (U+007B to U+007E) | ||
static bool _isPunctuation(int codeUnit) { | ||
return (codeUnit >= 33 && codeUnit <= 47) || | ||
(codeUnit >= 58 && codeUnit <= 64) || | ||
(codeUnit >= 91 && codeUnit <= 96) || | ||
(codeUnit >= 123 && codeUnit <= 126); | ||
} | ||
} | ||
|
||
class DapResolver { | ||
/// default constructor with http client and a web5 did resolver | ||
/// can construct yourself for mocking purposes etc. | ||
/// getRegistryEndpoint() -> resolve registry did, find DAPRegistry service endpoint | ||
/// dereferenceHandle() -> makes GET request to registry endpoint to get DAP's DID | ||
/// resolveDid() -> resolve DAP's DID, get back did document | ||
/// getMoneyAddresses(dap) -> getRegistryEndpoint() -> dereferenceHandle() -> resolveDid() -> parse out money addresses from did document -> return list of money addrrsses | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,76 @@ | ||
import 'package:dap/dap.dart'; | ||
import 'package:test/test.dart'; | ||
|
||
void main() { | ||
group('A group of tests', () { | ||
final dap = Dap(); | ||
class TestVector { | ||
final String input; | ||
final Dap? expected; | ||
final bool err; | ||
|
||
TestVector({required this.input, this.expected, required this.err}); | ||
} | ||
|
||
setUp(() { | ||
// Additional setup goes here. | ||
}); | ||
void main() { | ||
group('Dap.parse', () { | ||
final vectors = [ | ||
TestVector( | ||
input: '@moegrammer/didpay.me', | ||
expected: Dap(handle: 'moegrammer', domain: 'didpay.me'), | ||
err: false, | ||
), | ||
TestVector( | ||
input: '@moegrammer/www.linkedin.com', | ||
expected: Dap(handle: 'moegrammer', domain: 'www.linkedin.com'), | ||
err: false, | ||
), | ||
TestVector( | ||
input: '@💰bags/cash.app', | ||
expected: Dap(handle: '💰bags', domain: 'cash.app'), | ||
err: false, | ||
), | ||
TestVector( | ||
input: 'doodoo', | ||
err: true, | ||
), | ||
TestVector( | ||
input: 'doo@[email protected]', | ||
err: true, | ||
), | ||
TestVector( | ||
input: 'doodoo@', | ||
err: true, | ||
), | ||
TestVector( | ||
input: '@', | ||
err: true, | ||
), | ||
TestVector( | ||
input: '@@', | ||
err: true, | ||
), | ||
TestVector( | ||
input: '/', | ||
err: true, | ||
), | ||
TestVector( | ||
input: '@/', | ||
err: true, | ||
), | ||
TestVector( | ||
input: '[email protected]', | ||
err: true, | ||
), | ||
]; | ||
|
||
test('First Test', () { | ||
expect(dap.hehe, isTrue); | ||
}); | ||
for (var vector in vectors) { | ||
test(vector.input, () { | ||
if (vector.err) { | ||
expect(() => Dap.parse(vector.input), throwsFormatException); | ||
} else { | ||
final actual = Dap.parse(vector.input); | ||
expect(actual.handle, vector.expected!.handle); | ||
expect(actual.domain, vector.expected!.domain); | ||
} | ||
}); | ||
} | ||
}); | ||
} |