feat: introduce models to represent atproto id

This commit is contained in:
Stevan Freeborn committed 2026-08-28 09:14:52 -05:00
1 parent e3b4194653
commit 716d79739c
2 files changed
+151

No files matched your search

@@ -0,0 +1,132 @@
namespace StevanFreeborn.Atproto.IdentityResolver;
/// <summary>
///
/// </summary>
public abstract class AtprotoId
{
/// <summary>
///
/// </summary>
public string Value { get; protected set; } = string.Empty;
/// <summary>
///
/// </summary>
public static AtprotoId From(string atprotoId)
{
if (string.IsNullOrEmpty(atprotoId))
{
throw new ArgumentException("Cannot be null or empty", nameof(atprotoId));
}
if (IsDomain(atprotoId))
{
return Handle.From(atprotoId);
}
return Did.From(atprotoId);
}
private static bool IsDomain(string did)
{
var hostType = Uri.CheckHostName(did);
return hostType == UriHostNameType.Dns && did.Contains('.', StringComparison.OrdinalIgnoreCase);
}
}
/// <summary>
///
/// </summary>
public class Did : AtprotoId
{
private static readonly string[] ValidPrefixes = ["did:web:", "did:plc:"];
/// <summary>
///
/// </summary>
public string Method { get; private set; }
private Did(string did)
{
if (string.IsNullOrEmpty(did))
{
throw new ArgumentException("Cannot be null or empty", nameof(did));
}
if (TryParseWithoutPrefix(did, out var prefix, out var didWithoutPrefix) is false)
{
throw new ArgumentException($"Must be did with one of these methods: {string.Join(',', ValidPrefixes)}", nameof(did));
}
if (string.IsNullOrEmpty(didWithoutPrefix))
{
throw new ArgumentException("Must not be null or empty without did method prefix", nameof(did));
}
// Method should probably be a strongly
// typed object
Method = prefix.Contains("web", StringComparison.OrdinalIgnoreCase) ? "web" : "plc";
Value = did;
}
/// <summary>
///
/// </summary>
public static new Did From(string did)
{
return new(did);
}
private static bool TryParseWithoutPrefix(
string did,
out string prefix,
out string didWithoutPrefix
)
{
prefix = string.Empty;
didWithoutPrefix = string.Empty;
foreach (var validPrefix in ValidPrefixes)
{
if (did.StartsWith(validPrefix, StringComparison.OrdinalIgnoreCase))
{
prefix = did[..validPrefix.Length];
didWithoutPrefix = did[validPrefix.Length..];
return true;
}
}
return false;
}
}
/// <summary>
///
/// </summary>
public class Handle : AtprotoId
{
private Handle(string handle)
{
if (string.IsNullOrEmpty(handle))
{
throw new ArgumentException("Cannot be null or empty", nameof(handle));
}
if (handle.StartsWith('@'))
{
throw new ArgumentException("Cannot start with @", nameof(handle));
}
Value = handle;
}
/// <summary>
///
/// </summary>
public static new Handle From(string handle)
{
return new(handle);
}
}