Skip to content

Commit

Permalink
Merge pull request #74 from jscarle/feature/aot
Browse files Browse the repository at this point in the history
Added support for AOT.
  • Loading branch information
jscarle committed Mar 15, 2024
2 parents 19596b4 + 9875a52 commit 1290c0d
Show file tree
Hide file tree
Showing 36 changed files with 245 additions and 175 deletions.
10 changes: 2 additions & 8 deletions OnePassword.NET.Tests/Common/CommonExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,12 @@ internal static class CommonExtensions
internal static string ToEnumString<TField>(this TField field)
where TField : Enum
{
var fieldInfo = typeof(TField).GetField(field.ToString());
if (fieldInfo is null)
throw new ArgumentException("Could not find enum field.", nameof(field));

var fieldInfo = typeof(TField).GetField(field.ToString()) ?? throw new ArgumentException("Could not find enum field.", nameof(field));
var attributes = (EnumMemberAttribute[])fieldInfo.GetCustomAttributes(typeof(EnumMemberAttribute), false);
if (attributes.Length == 0)
throw new NotImplementedException($"{nameof(EnumMemberAttribute)} has not been defined for this field.");

var iconName = attributes[0].Value;
if (iconName is null)
throw new NotImplementedException($"{nameof(EnumMemberAttribute)} has not been defined for this field.");

var iconName = attributes[0].Value ?? throw new NotImplementedException($"{nameof(EnumMemberAttribute)} has not been defined for this field.");
return iconName;
}

Expand Down
4 changes: 1 addition & 3 deletions OnePassword.NET.Tests/Common/TestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,7 @@ await using (var fileStream = new FileStream(zipFileName, FileMode.Create))
await stream.CopyToAsync(fileStream);

using var zipArchive = ZipFile.Open(zipFileName, ZipArchiveMode.Read);
var entry = zipArchive.GetEntry(ExecutableName);
if (entry is null)
throw new IOException($"Could not find {ExecutableName} in the zip file.");
var entry = zipArchive.GetEntry(ExecutableName) ?? throw new IOException($"Could not find {ExecutableName} in the zip file.");
entry.ExtractToFile(extractFilename, true);

OnePassword = new OnePasswordManager(options =>
Expand Down
36 changes: 24 additions & 12 deletions OnePassword.NET/Accounts/Account.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,28 @@ public sealed class Account : IAccount
/// <inheritdoc />
[JsonInclude]
[JsonPropertyName("account_uuid")]
public string Id { get; internal init; } = "";
public string Id { get; internal set; } = "";

/// <summary>The account shorthand.</summary>
[JsonInclude]
[JsonPropertyName("shorthand")]
public string Shorthand { get; internal init; } = "";
public string Shorthand { get; internal set; } = "";

/// <summary>The account URL.</summary>
[JsonInclude]
[JsonPropertyName("url")]
[SuppressMessage("Design", "CA1056:URI-like properties should not be strings")]
public string Url { get; internal init; } = "";
public string Url { get; internal set; } = "";

/// <summary>The user ID for the user associated with the account.</summary>
[JsonInclude]
[JsonPropertyName("user_uuid")]
public string UserId { get; internal init; } = "";
public string UserId { get; internal set; } = "";

/// <summary>The email address for the user associated with the account.</summary>
[JsonInclude]
[JsonPropertyName("email")]
public string Email { get; internal init; } = "";
public string Email { get; internal set; } = "";

/// <inheritdoc />
public void Deconstruct(out string id, out string name)
Expand All @@ -45,37 +45,37 @@ public void Deconstruct(out string id, out string name)
/// <param name="a">The first account to compare.</param>
/// <param name="b">The second account to compare.</param>
/// <returns>True if the accounts are equal, false otherwise.</returns>
public static bool operator ==(Account a, IAccount b) => a?.Equals(b) ?? false;
public static bool operator ==(Account? a, IAccount? b) => Equals(a, b);

/// <summary>Inequality operator.</summary>
/// <param name="a">The first account to compare.</param>
/// <param name="b">The second account to compare.</param>
/// <returns>True if the accounts are not equal, false otherwise.</returns>
public static bool operator !=(Account a, IAccount b) => !a?.Equals(b) ?? false;
public static bool operator !=(Account? a, IAccount? b) => !Equals(a, b);

/// <summary>Less than operator.</summary>
/// <param name="a">The first account to compare.</param>
/// <param name="b">The second account to compare.</param>
/// <returns>True if the first account is less than the second one, false otherwise.</returns>
public static bool operator <(Account a, IAccount b) => a?.CompareTo(b) < 0;
public static bool operator <(Account? a, IAccount? b) => NullSafeCompareTo(a, b) < 0;

/// <summary>Less than or equal to operator.</summary>
/// <param name="a">The first account to compare.</param>
/// <param name="b">The second account to compare.</param>
/// <returns>True if the first account is less than or equal to the second one, false otherwise.</returns>
public static bool operator <=(Account a, IAccount b) => a?.CompareTo(b) <= 0;
public static bool operator <=(Account? a, IAccount? b) => NullSafeCompareTo(a, b) <= 0;

/// <summary>Greater than operator.</summary>
/// <param name="a">The first account to compare.</param>
/// <param name="b">The second account to compare.</param>
/// <returns>True if the first account is greater than the second one, false otherwise.</returns>
public static bool operator >(Account a, IAccount b) => a?.CompareTo(b) > 0;
public static bool operator >(Account? a, IAccount? b) => NullSafeCompareTo(a, b) > 0;

/// <summary>Greater than or equal to operator.</summary>
/// <param name="a">The first account to compare.</param>
/// <param name="b">The second account to compare.</param>
/// <returns>True if the first account is greater than or equal to the second one, false otherwise.</returns>
public static bool operator >=(Account a, IAccount b) => a?.CompareTo(b) >= 0;
public static bool operator >=(Account? a, IAccount? b) => NullSafeCompareTo(a, b) >= 0;

/// <inheritdoc />
public override bool Equals(object? obj) => ReferenceEquals(this, obj) || obj is IAccount other && Equals(other);
Expand Down Expand Up @@ -109,7 +109,10 @@ public int CompareTo(IAccount? other)
}

/// <inheritdoc />
public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Id);
public override int GetHashCode() =>
// ReSharper disable once NonReadonlyMemberInGetHashCode
// Id can only be set by internal methods.
StringComparer.OrdinalIgnoreCase.GetHashCode(Id);

private int CompareTo(Account? other)
{
Expand All @@ -118,4 +121,13 @@ private int CompareTo(Account? other)
}

private int CompareTo(AccountDetails? other) => other is null ? 1 : string.Compare(Shorthand, other.Domain, StringComparison.Ordinal);

private static int NullSafeCompareTo(Account? a, IAccount? b)
{
if (a is not null)
return b is null ? 1 : a.CompareTo(b);
if (b is null)
return 0;
return -1;
}
}
38 changes: 25 additions & 13 deletions OnePassword.NET/Accounts/AccountDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,32 @@ public sealed class AccountDetails : IAccount
/// <inheritdoc />
[JsonInclude]
[JsonPropertyName("id")]
public string Id { get; internal init; } = "";
public string Id { get; internal set; } = "";

/// <summary>The account name.</summary>
[JsonInclude]
[JsonPropertyName("name")]
public string Name { get; internal init; } = "";
public string Name { get; internal set; } = "";

/// <summary>The account domain.</summary>
[JsonInclude]
[JsonPropertyName("domain")]
public string Domain { get; internal init; } = "";
public string Domain { get; internal set; } = "";

/// <summary>The account type.</summary>
[JsonInclude]
[JsonPropertyName("type")]
public AccountType Type { get; internal init; } = AccountType.Unknown;
public AccountType Type { get; internal set; } = AccountType.Unknown;

/// <summary>The state of the account.</summary>
[JsonInclude]
[JsonPropertyName("state")]
public State State { get; internal init; } = State.Unknown;
public State State { get; internal set; } = State.Unknown;

/// <summary>The date and time when the account was created.</summary>
[JsonInclude]
[JsonPropertyName("created_at")]
public DateTimeOffset Created { get; internal init; }
public DateTimeOffset Created { get; internal set; }

/// <inheritdoc />
public void Deconstruct(out string id, out string name)
Expand All @@ -49,37 +49,37 @@ public void Deconstruct(out string id, out string name)
/// <param name="a">The <see cref="AccountDetails" /> object.</param>
/// <param name="b">The <see cref="IAccount" /> object to compare.</param>
/// <returns>True if the <paramref name="a" /> is equal to <paramref name="b" />; otherwise, false.</returns>
public static bool operator ==(AccountDetails a, IAccount b) => a?.Equals(b) ?? false;
public static bool operator ==(AccountDetails? a, IAccount? b) => Equals(a, b);

/// <summary>Inequality operator.</summary>
/// <param name="a">The <see cref="AccountDetails" /> object.</param>
/// <param name="b">The <see cref="IAccount" /> object to compare.</param>
/// <returns>True if the <paramref name="a" /> is not equal to <paramref name="b" />; otherwise, false.</returns>
public static bool operator !=(AccountDetails a, IAccount b) => !a?.Equals(b) ?? false;
public static bool operator !=(AccountDetails? a, IAccount? b) => !Equals(a, b);

/// <summary>Less than operator.</summary>
/// <param name="a">The <see cref="AccountDetails" /> object.</param>
/// <param name="b">The <see cref="IAccount" /> object to compare.</param>
/// <returns>True if the <paramref name="a" /> is less than <paramref name="b" />; otherwise, false.</returns>
public static bool operator <(AccountDetails a, IAccount b) => a?.CompareTo(b) < 0;
public static bool operator <(AccountDetails? a, IAccount? b) => NullSafeCompareTo(a, b) < 0;

/// <summary>Less than or equal to operator.</summary>
/// <param name="a">The <see cref="AccountDetails" /> object.</param>
/// <param name="b">The <see cref="IAccount" /> object to compare.</param>
/// <returns>True if the <paramref name="a" /> is less than or equal to <paramref name="b" />; otherwise, false.</returns>
public static bool operator <=(AccountDetails a, IAccount b) => a?.CompareTo(b) <= 0;
public static bool operator <=(AccountDetails? a, IAccount? b) => NullSafeCompareTo(a, b) <= 0;

/// <summary>Greater than operator.</summary>
/// <param name="a">The <see cref="AccountDetails" /> object.</param>
/// <param name="b">The <see cref="IAccount" /> object to compare.</param>
/// <returns>True if the <paramref name="a" /> is greater than <paramref name="b" />; otherwise, false.</returns>
public static bool operator >(AccountDetails a, IAccount b) => a?.CompareTo(b) > 0;
public static bool operator >(AccountDetails? a, IAccount? b) => NullSafeCompareTo(a, b) > 0;

/// <summary>Greater than or equal to operator.</summary>
/// <param name="a">The <see cref="AccountDetails" /> object.</param>
/// <param name="b">The <see cref="IAccount" /> object to compare.</param>
/// <returns>True if the <paramref name="a" /> is greater than or equal to <paramref name="b" />; otherwise, false.</returns>
public static bool operator >=(AccountDetails a, IAccount b) => a?.CompareTo(b) >= 0;
public static bool operator >=(AccountDetails? a, IAccount? b) => NullSafeCompareTo(a, b) >= 0;

/// <inheritdoc />
public override bool Equals(object? obj) => ReferenceEquals(this, obj) || obj is IAccount other && Equals(other);
Expand Down Expand Up @@ -113,7 +113,10 @@ public int CompareTo(IAccount? other)
}

/// <inheritdoc />
public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Id);
public override int GetHashCode() =>
// ReSharper disable once NonReadonlyMemberInGetHashCode
// Id can only be set by internal methods.
StringComparer.OrdinalIgnoreCase.GetHashCode(Id);

private int CompareTo(Account? other) => other is null ? 1 : string.Compare(Domain, other.Shorthand, StringComparison.Ordinal);

Expand All @@ -122,4 +125,13 @@ private int CompareTo(AccountDetails? other)
if (other is null) return 1;
return ReferenceEquals(this, other) ? 0 : string.Compare(Domain, other.Domain, StringComparison.Ordinal);
}

private static int NullSafeCompareTo(AccountDetails? a, IAccount? b)
{
if (a is not null)
return b is null ? 1 : a.CompareTo(b);
if (b is null)
return 0;
return -1;
}
}
16 changes: 7 additions & 9 deletions OnePassword.NET/Common/CommonExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
namespace OnePassword.Common;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;

namespace OnePassword.Common;

/// <summary>
/// Common extensions methods.
Expand All @@ -13,21 +16,16 @@ internal static class CommonExtensions
/// <returns>A string representation of the enum field.</returns>
/// <exception cref="ArgumentNullException">Thrown when field is null.</exception>
/// <exception cref="NotImplementedException">Thrown when field is not correctly annotated with a <see cref="EnumMemberAttribute"/>.</exception>
[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2090:'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to 'target method'.", Justification = "https://github.com/dotnet/runtime/issues/97737")]
internal static string ToEnumString<TField>(this TField field)
where TField : Enum
{
var fieldInfo = typeof(TField).GetField(field.ToString());
if (fieldInfo is null)
throw new ArgumentNullException(nameof(field));

var fieldInfo = typeof(TField).GetField(field.ToString(), BindingFlags.Public | BindingFlags.Static) ?? throw new ArgumentNullException(nameof(field));
var attributes = (EnumMemberAttribute[])fieldInfo.GetCustomAttributes(typeof(EnumMemberAttribute), false);
if (attributes.Length == 0)
throw new NotImplementedException($"The field has not been annotated with a {nameof(EnumMemberAttribute)}.");

var value = attributes[0].Value;
if (value is null)
throw new NotImplementedException($"{nameof(EnumMemberAttribute)}.{nameof(EnumMemberAttribute.Value)} has not been set for this field.");

var value = attributes[0].Value ?? throw new NotImplementedException($"{nameof(EnumMemberAttribute)}.{nameof(EnumMemberAttribute.Value)} has not been set for this field.");
return value;
}

Expand Down
30 changes: 30 additions & 0 deletions OnePassword.NET/Common/JsonContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using OnePassword.Accounts;
using OnePassword.Documents;
using OnePassword.Groups;
using OnePassword.Items;
using OnePassword.Templates;
using OnePassword.Users;
using OnePassword.Vaults;

namespace OnePassword.Common;

[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ImmutableList<Account>))]
[JsonSerializable(typeof(AccountDetails))]
[JsonSerializable(typeof(ImmutableList<DocumentDetails>))]
[JsonSerializable(typeof(Document))]
[JsonSerializable(typeof(ImmutableList<Group>))]
[JsonSerializable(typeof(ImmutableList<VaultGroup>))]
[JsonSerializable(typeof(ImmutableList<UserGroup>))]
[JsonSerializable(typeof(GroupDetails))]
[JsonSerializable(typeof(ImmutableList<Item>))]
[JsonSerializable(typeof(Item))]
[JsonSerializable(typeof(ImmutableList<TemplateInfo>))]
[JsonSerializable(typeof(Template))]
[JsonSerializable(typeof(ImmutableList<User>))]
[JsonSerializable(typeof(ImmutableList<GroupUser>))]
[JsonSerializable(typeof(ImmutableList<VaultUser>))]
[JsonSerializable(typeof(UserDetails))]
[JsonSerializable(typeof(ImmutableList<Vault>))]
[JsonSerializable(typeof(VaultDetails))]
internal sealed partial class JsonContext : JsonSerializerContext;
8 changes: 5 additions & 3 deletions OnePassword.NET/Common/JsonStringEnumConverterEx.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using System.Reflection;

namespace OnePassword.Common;

Expand All @@ -8,17 +9,18 @@ namespace OnePassword.Common;
[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes")]
internal sealed class JsonStringEnumConverterEx<TEnum> : JsonConverter<TEnum> where TEnum : struct, Enum
{
private readonly Dictionary<TEnum, string> _enumToString = new();
private readonly Dictionary<string, TEnum> _stringToEnum = new();
private readonly Dictionary<TEnum, string> _enumToString = [];
private readonly Dictionary<string, TEnum> _stringToEnum = [];

/// <summary>Initializes a new instance of <see cref="JsonStringEnumConverterEx{TEnum}" />.</summary>
[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2090:'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to 'target method'.", Justification = "https://github.com/dotnet/runtime/issues/97737")]
public JsonStringEnumConverterEx()
{
foreach (var enumMemberValue in Enum.GetValues<TEnum>())
{
var enumMemberName = enumMemberValue.ToString();

var enumMemberAttribute = typeof(TEnum).GetMember(enumMemberName).FirstOrDefault()?.GetCustomAttributes(typeof(EnumMemberAttribute), false).Cast<EnumMemberAttribute>().FirstOrDefault();
var enumMemberAttribute = typeof(TEnum).GetMember(enumMemberName, BindingFlags.Public | BindingFlags.Static).FirstOrDefault()?.GetCustomAttributes(typeof(EnumMemberAttribute), false).Cast<EnumMemberAttribute>().FirstOrDefault();
if (enumMemberAttribute is { Value: not null })
{
var enumMemberString = enumMemberAttribute.Value.ToUpperInvariant().Replace(" ", "_", StringComparison.InvariantCulture);
Expand Down

0 comments on commit 1290c0d

Please sign in to comment.