Skip to content
This repository has been archived by the owner on Dec 12, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1 from TBD54566975/parse
Browse files Browse the repository at this point in the history
implement `Dap.parse`
  • Loading branch information
mistermoe authored Jun 24, 2024
2 parents 296bdc8 + 8959509 commit 727f587
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 14 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/test.yml
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
20 changes: 20 additions & 0 deletions Justfile
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
1 change: 1 addition & 0 deletions bin/.just-1.29.1.pkg
1 change: 1 addition & 0 deletions bin/just
7 changes: 5 additions & 2 deletions example/dap_example.dart
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
}
96 changes: 94 additions & 2 deletions lib/src/dap.dart
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
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version: 0.1.0
repository: https://github.com/TBD54566975/dap-dart

environment:
sdk: ^3.3.4
sdk: ^3.3.3

# Add regular dependencies here.
dependencies:
Expand Down
78 changes: 69 additions & 9 deletions test/dap_test.dart
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);
}
});
}
});
}

0 comments on commit 727f587

Please sign in to comment.