public class LinkIndexLinkChecker()

in src/Elastic.Markdown/Links/InboundLinks/LinkIndexLinkChecker.cs [14:134]


public class LinkIndexLinkChecker(ILoggerFactory logger)
{
	private readonly ILogger _logger = logger.CreateLogger<LinkIndexLinkChecker>();

	private sealed record RepositoryFilter
	{
		public string? LinksTo { get; init; }
		public string? LinksFrom { get; init; }

		public static RepositoryFilter None => new();
	}

	public async Task CheckAll(IDiagnosticsCollector collector, Cancel ctx)
	{
		var fetcher = new LinksIndexCrossLinkFetcher(logger);
		var resolver = new CrossLinkResolver(fetcher);
		var crossLinks = await resolver.FetchLinks(ctx);

		ValidateCrossLinks(collector, crossLinks, resolver, RepositoryFilter.None);
	}

	public async Task CheckRepository(IDiagnosticsCollector collector, string? toRepository, string? fromRepository, Cancel ctx)
	{
		var fetcher = new LinksIndexCrossLinkFetcher(logger);
		var resolver = new CrossLinkResolver(fetcher);
		var crossLinks = await resolver.FetchLinks(ctx);
		var filter = new RepositoryFilter
		{
			LinksTo = toRepository,
			LinksFrom = fromRepository
		};

		ValidateCrossLinks(collector, crossLinks, resolver, filter);
	}

	public async Task CheckWithLocalLinksJson(IDiagnosticsCollector collector, string repository, string localLinksJson, Cancel ctx)
	{
		var fetcher = new LinksIndexCrossLinkFetcher(logger);
		var resolver = new CrossLinkResolver(fetcher);
		// ReSharper disable once RedundantAssignment
		var crossLinks = await resolver.FetchLinks(ctx);
		if (string.IsNullOrEmpty(repository))
			throw new ArgumentNullException(nameof(repository));
		if (string.IsNullOrEmpty(localLinksJson))
			throw new ArgumentNullException(nameof(repository));

		_logger.LogInformation("Checking '{Repository}' with local '{LocalLinksJson}'", repository, localLinksJson);

		if (!Path.IsPathRooted(localLinksJson))
			localLinksJson = Path.Combine(Paths.WorkingDirectoryRoot.FullName, localLinksJson);

		try
		{
			var json = await File.ReadAllTextAsync(localLinksJson, ctx);
			var localLinkReference = LinkReference.Deserialize(json);
			crossLinks = resolver.UpdateLinkReference(repository, localLinkReference);
		}
		catch (Exception e)
		{
			_logger.LogError(e, "Failed to read {LocalLinksJson}", localLinksJson);
			throw;
		}

		_logger.LogInformation("Validating all cross links to {Repository}:// from all repositories published to link-index.json", repository);
		var filter = new RepositoryFilter
		{
			LinksTo = repository
		};

		ValidateCrossLinks(collector, crossLinks, resolver, filter);
	}

	private void ValidateCrossLinks(
		IDiagnosticsCollector collector,
		FetchedCrossLinks crossLinks,
		CrossLinkResolver resolver,
		RepositoryFilter filter
	)
	{
		foreach (var (repository, linkReference) in crossLinks.LinkReferences)
		{
			if (!string.IsNullOrEmpty(filter.LinksTo))
				_logger.LogInformation("Validating '{CurrentRepository}://' links in {TargetRepository}", filter.LinksTo, repository);
			else if (!string.IsNullOrEmpty(filter.LinksFrom))
			{
				if (repository != filter.LinksFrom)
					continue;
				_logger.LogInformation("Validating cross_links from {TargetRepository}", filter.LinksFrom);
			}
			else
				_logger.LogInformation("Validating all cross_links in {Repository}", repository);

			foreach (var crossLink in linkReference.CrossLinks)
			{
				// if we are filtering we only want errors from inbound links to a certain
				// repository
				var uri = new Uri(crossLink);
				if (filter.LinksTo != null && uri.Scheme != filter.LinksTo)
					continue;

				var linksJson = $"https://elastic-docs-link-index.s3.us-east-2.amazonaws.com/elastic/{uri.Scheme}/main/links.json";
				if (crossLinks.LinkIndexEntries.TryGetValue(uri.Scheme, out var linkIndexEntry))
					linksJson = $"https://elastic-docs-link-index.s3.us-east-2.amazonaws.com/{linkIndexEntry.Path}";
				_ = resolver.TryResolve(s =>
				{
					if (s.Contains("is not a valid link in the"))
					{
						//
						var error = $"'elastic/{repository}' links to unknown file: " + s;
						error = error.Replace("is not a valid link in the", "in the");
						collector.EmitError(linksJson, error);
						return;
					}

					collector.EmitError(repository, s);
				}, s => collector.EmitWarning(linksJson, s), uri, out _);
			}
		}
		// non-strict for now
	}
}