Skip to content

Commit

Permalink
Replaced Immutable collections with ReadOnly ones.
Browse files Browse the repository at this point in the history
  • Loading branch information
waylaa committed Dec 29, 2023
1 parent 2048735 commit 1086b61
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 108 deletions.
7 changes: 4 additions & 3 deletions OsuCollectionDownloader/Factories/DownloadProcessorFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
Expand All @@ -12,7 +13,7 @@ namespace OsuCollectionDownloader.Factories;

internal sealed class DownloadProcessorFactory
{
private static readonly FrozenDictionary<Type, Delegate> _constructors = new Dictionary<Type, Delegate>
private static readonly ReadOnlyDictionary<Type, Delegate> _constructors = new Dictionary<Type, Delegate>
{
{
typeof(SequentialDownloadProcessor),
Expand All @@ -23,10 +24,10 @@ internal sealed class DownloadProcessorFactory
new DownloadProcessorConstructor<ConcurrentDownloadProcessor>((options, clientFactory, logger) => new ConcurrentDownloadProcessor(options, clientFactory, logger))
}

}.ToFrozenDictionary();
}.AsReadOnly();

private delegate T DownloadProcessorConstructor<T>(DownloadProcessorBaseOptions options, IHttpClientFactory clientFactory, ILogger<T> logger);

internal static T Create<T>(DownloadProcessorBaseOptions options, IHttpClientFactory clientFactory, ILogger<T> logger) where T : class
=> ((DownloadProcessorConstructor<T>)_constructors.GetValueRefOrNullRef(typeof(T)))(options, clientFactory, logger); // Casts the ref delegate and invokes it.
=> ((DownloadProcessorConstructor<T>)_constructors.GetValueOrDefault(typeof(T))!)(options, clientFactory, logger); // Casts the delegate and invokes it.
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ namespace OsuCollectionDownloader.Json.Contexts;
[JsonSerializable(typeof(BpmSpread))]
[JsonSerializable(typeof(PartialBeatmapset))]
[JsonSerializable(typeof(PartialBeatmap))]
[JsonSerializable(typeof(IImmutableList<PartialBeatmap>))]
[JsonSerializable(typeof(IImmutableList<Beatmapset>))]
[JsonSerializable(typeof(IImmutableList<Comment>))]
[JsonSerializable(typeof(IImmutableList<int>))]
[JsonSerializable(typeof(IImmutableList<object>))]
[JsonSerializable(typeof(IReadOnlyList<PartialBeatmap>))]
[JsonSerializable(typeof(IReadOnlyList<Beatmapset>))]
[JsonSerializable(typeof(IReadOnlyList<Comment>))]
[JsonSerializable(typeof(IReadOnlyList<int>))]
[JsonSerializable(typeof(IReadOnlyList<object>))]
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(int))]
internal sealed partial class FetchedCollectionMetadataSerializationContext : JsonSerializerContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ namespace OsuCollectionDownloader.Json.Contexts;
[JsonSerializable(typeof(Beatmapset))]
[JsonSerializable(typeof(Beatmap))]
[JsonSerializable(typeof(Availability))]
[JsonSerializable(typeof(IImmutableList<Beatmap>))]
[JsonSerializable(typeof(IImmutableList<int>))]
[JsonSerializable(typeof(IImmutableList<int>))]
[JsonSerializable(typeof(IReadOnlyList<Beatmap>))]
[JsonSerializable(typeof(IReadOnlyList<int>))]
[JsonSerializable(typeof(IReadOnlyList<int>))]
[JsonSerializable(typeof(DateTime))]
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(bool))]
Expand Down
8 changes: 4 additions & 4 deletions OsuCollectionDownloader/Json/Models/FetchedCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ internal sealed record Beatmapset
[property: JsonPropertyName("discussion_enabled")] bool DiscussionEnabled,
[property: JsonPropertyName("is_scoreable")] bool IsScoreable,
[property: JsonPropertyName("can_be_hyped")] bool CanBeHyped,
[property: JsonPropertyName("ratings")] IImmutableList<int> Ratings,
[property: JsonPropertyName("ratings")] IReadOnlyList<int> Ratings,
[property: JsonPropertyName("ranked")] int Ranked,
[property: JsonPropertyName("id")] int Id,
[property: JsonPropertyName("legacy_thread_url")] string LegacyThreadUrl,
Expand Down Expand Up @@ -92,8 +92,8 @@ internal sealed record Covers

internal sealed record Failtimes
(
[property: JsonPropertyName("exit")] IImmutableList<int> Exit,
[property: JsonPropertyName("fail")] IImmutableList<int> Fail
[property: JsonPropertyName("exit")] IReadOnlyList<int> Exit,
[property: JsonPropertyName("fail")] IReadOnlyList<int> Fail
);

internal sealed record NominationsSummary
Expand All @@ -106,5 +106,5 @@ internal sealed record FetchedCollection
(
[property: JsonPropertyName("nextPageCursor")] int? NextPageCursor,
[property: JsonPropertyName("hasMore")] bool HasMore,
[property: JsonPropertyName("beatmaps")] IImmutableList<Beatmap> Beatmaps
[property: JsonPropertyName("beatmaps")] IReadOnlyList<Beatmap> Beatmaps
);
12 changes: 6 additions & 6 deletions OsuCollectionDownloader/Json/Models/FetchedCollectionMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal sealed record PartialBeatmap

internal sealed record PartialBeatmapset
(
[property: JsonPropertyName("beatmaps")] IImmutableList<PartialBeatmap> Beatmaps,
[property: JsonPropertyName("beatmaps")] IReadOnlyList<PartialBeatmap> Beatmaps,
[property: JsonPropertyName("id")] int Id
);

Expand All @@ -38,7 +38,7 @@ internal sealed record BpmSpread
internal sealed record Comment
(
[property: JsonPropertyName("date")] Date Date,
[property: JsonPropertyName("upvotes")] IImmutableList<int> Upvotes,
[property: JsonPropertyName("upvotes")] IReadOnlyList<int> Upvotes,
[property: JsonPropertyName("id")] string Id,
[property: JsonPropertyName("message")] string Message,
[property: JsonPropertyName("userId")] int UserId,
Expand Down Expand Up @@ -94,14 +94,14 @@ internal sealed record FetchedCollectionMetadata
[property: JsonPropertyName("beatmapCount")] int BeatmapCount,
[property: JsonPropertyName("dateLastModified")] DateLastModified DateLastModified,
[property: JsonPropertyName("unsubmittedBeatmapCount")] int UnsubmittedBeatmapCount,
[property: JsonPropertyName("unknownChecksums")] IImmutableList<object> UnknownChecksums,
[property: JsonPropertyName("beatmapsets")] IImmutableList<Beatmapset> Beatmapsets,
[property: JsonPropertyName("unknownChecksums")] IReadOnlyList<object> UnknownChecksums,
[property: JsonPropertyName("beatmapsets")] IReadOnlyList<Beatmapset> Beatmapsets,
[property: JsonPropertyName("modes")] Modes Modes,
[property: JsonPropertyName("difficultySpread")] DifficultySpread DifficultySpread,
[property: JsonPropertyName("bpmSpread")] BpmSpread BpmSpread,
[property: JsonPropertyName("description")] string Description,
[property: JsonPropertyName("comments")] IImmutableList<Comment> Comments,
[property: JsonPropertyName("favouritedBy")] IImmutableList<int> FavouritedBy,
[property: JsonPropertyName("comments")] IReadOnlyList<Comment> Comments,
[property: JsonPropertyName("favouritedBy")] IReadOnlyList<int> FavouritedBy,
[property: JsonPropertyName("favourites")] int Favourites
);

Expand Down
93 changes: 44 additions & 49 deletions OsuCollectionDownloader/Processors/ConcurrentDownloadProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Microsoft.Extensions.Caching.Memory;
using System.Collections.Frozen;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.ObjectModel;

namespace OsuCollectionDownloader.Processors;

Expand Down Expand Up @@ -44,71 +45,65 @@ internal override async Task DownloadAsync(CancellationToken token)

List<Beatmap> downloadedBeatmapsets = [];

using (IMemoryCache directoryCache = Program.Services.GetRequiredService<IMemoryCache>())
{
FrozenSet<string> cache = directoryCache.Set("Songs", new HashSet<string>(Directory.EnumerateDirectories(Options.ExtractionDirectory), StringComparer.OrdinalIgnoreCase).ToFrozenSet());
IReadOnlyList<string> cachedBeatmapFolders = Program.Services
.GetRequiredService<IMemoryCache>()
.Set("Songs", Directory.EnumerateDirectories(Options.ExtractionDirectory).ToList().AsReadOnly());

MirrorChain mirrorChain =
[
new NerinyanHandler(Client),
new ChimuHandler(Client),
new OsuDirectHandler(Client),
];
MirrorChain mirrorChain =
[
new NerinyanHandler(Client),
new ChimuHandler(Client),
new OsuDirectHandler(Client),
];

using SemaphoreSlim concurrencyLimiter = new(initialCount: 2);
using SemaphoreSlim concurrencyLimiter = new(initialCount: 2);

ImmutableArray<Task> downloads = beatmapsResult.Value.DistinctBy(x => x.BeatmapsetId).Select(async beatmap =>
{
await concurrencyLimiter.WaitAsync(token);
IReadOnlyList<Task> downloads = beatmapsResult.Value.DistinctBy(x => x.BeatmapsetId).Select(async beatmap =>
{
await concurrencyLimiter.WaitAsync(token);

string beatmapFileName = $"{beatmap.BeatmapsetId} {beatmap.Beatmapset.Artist} - {beatmap.Beatmapset.Title}.osz";
string beatmapFilePath = Path.Combine(Options.ExtractionDirectory, beatmapFileName.ReplaceInvalidPathChars());
string beatmapDirectory = Path.Combine(Options.ExtractionDirectory, Path.GetFileNameWithoutExtension(beatmapFileName.ReplaceInvalidPathChars()));
string beatmapFileName = $"{beatmap.BeatmapsetId} {beatmap.Beatmapset.Artist} - {beatmap.Beatmapset.Title}.osz";
string beatmapFilePath = Path.Combine(Options.ExtractionDirectory, beatmapFileName.ReplaceInvalidPathChars());
string beatmapDirectory = Path.Combine(Options.ExtractionDirectory, Path.GetFileNameWithoutExtension(beatmapFileName.ReplaceInvalidPathChars()));

if (cache.Contains(beatmapDirectory))
{
downloadedBeatmapsets.Add(beatmap);
logger.AlreadyExists(Path.GetFileNameWithoutExtension(beatmapFileName));
if (cachedBeatmapFolders.Contains(beatmapDirectory))
{
downloadedBeatmapsets.Add(beatmap);
logger.AlreadyExists(Path.GetFileNameWithoutExtension(beatmapFileName));

concurrencyLimiter.Release();
return;
}
concurrencyLimiter.Release();
return;
}

Result<bool> downloadResult = await mirrorChain.HandleAsync
(
beatmapFilePath,
beatmap.BeatmapsetId,
token
);
Result<bool> downloadResult = await mirrorChain.HandleAsync(beatmapFilePath, beatmap.BeatmapsetId, token);

if (!downloadResult.IsSucessfulWithValue || !downloadResult.Value)
{
logger.UnsuccessfulDownload(Path.GetFileNameWithoutExtension(beatmapFileName));
if (!downloadResult.IsSucessfulWithValue || !downloadResult.Value)
{
logger.UnsuccessfulDownload(Path.GetFileNameWithoutExtension(beatmapFileName));

concurrencyLimiter.Release();
return;
}
concurrencyLimiter.Release();
return;
}

Result<bool> extractionResult = Extract(beatmapFilePath);
File.Delete(beatmapFilePath);
Result<bool> extractionResult = Extract(beatmapFilePath);
File.Delete(beatmapFilePath);

if (!extractionResult.IsSucessfulWithValue || !extractionResult.Value)
{
logger.ExtractionFailure(Path.GetFileNameWithoutExtension(beatmapFileName));
if (!extractionResult.IsSucessfulWithValue || !extractionResult.Value)
{
logger.ExtractionFailure(Path.GetFileNameWithoutExtension(beatmapFileName));

concurrencyLimiter.Release();
return;
}
concurrencyLimiter.Release();
return;
}

downloadedBeatmapsets.Add(beatmap);
logger.SuccessfulDownload(Path.GetFileNameWithoutExtension(beatmapFileName));
downloadedBeatmapsets.Add(beatmap);
logger.SuccessfulDownload(Path.GetFileNameWithoutExtension(beatmapFileName));

concurrencyLimiter.Release();
concurrencyLimiter.Release();

}).ToImmutableArray();
}).ToList().AsReadOnly();

await Task.WhenAll(downloads);
}
await Task.WhenAll(downloads);

logger.DownloadFinished();

Expand Down
75 changes: 37 additions & 38 deletions OsuCollectionDownloader/Processors/SequentialDownloadProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,50 +44,49 @@ internal override async Task DownloadAsync(CancellationToken token)

List<Beatmap> downloadedBeatmapsets = [];

using (IMemoryCache directoryCache = Program.Services.GetRequiredService<IMemoryCache>())
IReadOnlyList<string> cachedBeatmapFolders = Program.Services
.GetRequiredService<IMemoryCache>()
.Set("Songs", Directory.EnumerateDirectories(Options.ExtractionDirectory).ToList().AsReadOnly());

MirrorChain mirrorChain =
[
new NerinyanHandler(Client),
new ChimuHandler(Client),
new OsuDirectHandler(Client),
];

foreach (Beatmap beatmap in beatmapsResult.Value.DistinctBy(x => x.BeatmapsetId))
{
FrozenSet<string> cache = directoryCache.Set("Songs", new HashSet<string>(Directory.EnumerateDirectories(Options.ExtractionDirectory), StringComparer.OrdinalIgnoreCase).ToFrozenSet());
string beatmapFileName = $"{beatmap.BeatmapsetId} {beatmap.Beatmapset.Artist} - {beatmap.Beatmapset.Title}.osz";
string beatmapFilePath = Path.Combine(Options.ExtractionDirectory, beatmapFileName.ReplaceInvalidPathChars());
string beatmapDirectory = Path.Combine(Options.ExtractionDirectory, Path.GetFileNameWithoutExtension(beatmapFileName.ReplaceInvalidPathChars()));

MirrorChain mirrorChain =
[
new NerinyanHandler(Client),
new ChimuHandler(Client),
new OsuDirectHandler(Client),
];
if (cachedBeatmapFolders.Contains(beatmapDirectory))
{
downloadedBeatmapsets.Add(beatmap);
logger.AlreadyExists(Path.GetFileNameWithoutExtension(beatmapFileName));

foreach (Beatmap beatmap in beatmapsResult.Value.DistinctBy(x => x.BeatmapsetId))
continue;
}

Result<bool> downloadResult = await mirrorChain.HandleAsync(beatmapFilePath, beatmap.BeatmapsetId, token);
if (!downloadResult.IsSucessfulWithValue || !downloadResult.Value)
{
string beatmapFileName = $"{beatmap.BeatmapsetId} {beatmap.Beatmapset.Artist} - {beatmap.Beatmapset.Title}.osz";
string beatmapFilePath = Path.Combine(Options.ExtractionDirectory, beatmapFileName.ReplaceInvalidPathChars());
string beatmapDirectory = Path.Combine(Options.ExtractionDirectory, Path.GetFileNameWithoutExtension(beatmapFileName.ReplaceInvalidPathChars()));

if (cache.Contains(beatmapDirectory))
{
downloadedBeatmapsets.Add(beatmap);
logger.AlreadyExists(Path.GetFileNameWithoutExtension(beatmapFileName));

continue;
}

Result<bool> downloadResult = await mirrorChain.HandleAsync(beatmapFilePath, beatmap.BeatmapsetId, token);
if (!downloadResult.IsSucessfulWithValue || !downloadResult.Value)
{
logger.UnsuccessfulDownload(Path.GetFileNameWithoutExtension(beatmapFileName));
continue;
}

Result<bool> extractionResult = Extract(beatmapFilePath);
File.Delete(beatmapFilePath);

if (!extractionResult.IsSucessfulWithValue || !extractionResult.Value)
{
logger.ExtractionFailure(Path.GetFileNameWithoutExtension(beatmapFileName));
continue;
}
logger.UnsuccessfulDownload(Path.GetFileNameWithoutExtension(beatmapFileName));
continue;
}

downloadedBeatmapsets.Add(beatmap);
logger.SuccessfulDownload(Path.GetFileNameWithoutExtension(beatmapFileName));
Result<bool> extractionResult = Extract(beatmapFilePath);
File.Delete(beatmapFilePath);

if (!extractionResult.IsSucessfulWithValue || !extractionResult.Value)
{
logger.ExtractionFailure(Path.GetFileNameWithoutExtension(beatmapFileName));
continue;
}

downloadedBeatmapsets.Add(beatmap);
logger.SuccessfulDownload(Path.GetFileNameWithoutExtension(beatmapFileName));
}

logger.DownloadFinished();
Expand Down

0 comments on commit 1086b61

Please sign in to comment.