using MaBibli.Shared.Dtos;
namespace MaBibli.Api.Services.Isbn;
///
/// Transformation d'une édition OpenLibrary en candidat. Fonctions pures, testables sans réseau.
///
public static class OpenLibraryMapper
{
///
/// Clés d'auteurs portées par l'édition elle-même (/authors/OL…A), éventuellement vide.
///
///
/// Ces clés ne sont pas des noms : chacune impose un appel à
/// /authors/{id}.json. Oublier ce second appel donne un titre rempli et un auteur vide
/// — symptôme exact observé chez BookLogr.
///
public static IReadOnlyList ClesAuteurs(OpenLibraryEdition edition) =>
edition.Authors?
.Select(a => a.Key)
.Where(k => !string.IsNullOrWhiteSpace(k))
.Select(k => k!)
.ToList() ?? [];
/// Clés d'auteurs portées par l'œuvre, utilisées en repli.
public static IReadOnlyList ClesAuteurs(OpenLibraryWork work) =>
work.Authors?
.Select(a => a.Author?.Key)
.Where(k => !string.IsNullOrWhiteSpace(k))
.Select(k => k!)
.ToList() ?? [];
/// Clé de l'œuvre rattachée (/works/OL…W), ou null.
public static string? CleOeuvre(OpenLibraryEdition edition) =>
edition.Works?.Select(w => w.Key).FirstOrDefault(k => !string.IsNullOrWhiteSpace(k));
/// Nom affichable d'un auteur OpenLibrary.
public static string? Nom(OpenLibraryAuthor auteur)
{
var nom = auteur.Name ?? auteur.PersonalName;
return string.IsNullOrWhiteSpace(nom) ? null : nom.Trim();
}
///
/// Assemble le candidat final. vient des appels
/// /authors/{id}.json déjà effectués par le client.
///
public static CandidatLivre? VersCandidat(
OpenLibraryEdition edition,
IReadOnlyList nomsAuteurs,
string isbnInterroge,
string? urlCouverture)
{
var titre = edition.Title?.Trim();
if (string.IsNullOrEmpty(titre))
{
return null;
}
if (!string.IsNullOrWhiteSpace(edition.Subtitle))
{
titre = $"{titre} : {edition.Subtitle.Trim()}";
}
// Les noms résolus par /authors/{id}.json arrivent déjà séparés. En dernier repli la
// mention de responsabilité n'est qu'une phrase : on la garde entière plutôt que de
// parier sur ses virgules, qui séparent aussi bien deux auteurs qu'un « Zola, Émile ».
var auteurs = nomsAuteurs.Count > 0
? nomsAuteurs
: Liste(NettoyerByStatement(edition.ByStatement));
return new CandidatLivre
{
Titre = titre,
Auteurs = auteurs,
Editeur = edition.Publishers?.FirstOrDefault()?.Trim().Trim('[', ']'),
Annee = string.IsNullOrWhiteSpace(edition.PublishDate) ? null : edition.PublishDate.Trim(),
Langue = edition.Languages?
.Select(l => l.Key)
.FirstOrDefault(k => !string.IsNullOrWhiteSpace(k))?
.Replace("/languages/", string.Empty),
CoverUrl = urlCouverture,
Source = SourceMetadonnees.OpenLibrary,
IsbnInterroge = isbnInterroge,
IdentifiantSource = edition.Key,
UrlNotice = UrlNotice(edition.Key),
};
}
private static string? UrlNotice(string? cle) =>
string.IsNullOrWhiteSpace(cle)
? null
: $"https://openlibrary.org/{cle.TrimStart('/')}";
private static IReadOnlyList Liste(string? valeur) =>
string.IsNullOrWhiteSpace(valeur) ? [] : [valeur];
///
/// Dernier repli quand aucun auteur n'est résolvable : la mention de responsabilité brute,
/// débarrassée de sa ponctuation finale (« Thomas H. Cormen ... [et al.]. »).
///
private static string? NettoyerByStatement(string? brut)
{
var s = brut?.Trim();
if (string.IsNullOrEmpty(s))
{
return null;
}
var i = s.IndexOf(" ... ", StringComparison.Ordinal);
if (i >= 0)
{
s = s[..i];
}
s = s.TrimEnd('.', ' ', ',');
return s.Length == 0 ? null : s;
}
}