Newer
Older
dub_jkp / source / dub / packagesuppliers / fallback.d
  1. module dub.packagesuppliers.fallback;
  2.  
  3. import dub.packagesuppliers.packagesupplier;
  4. import std.typecons : AutoImplement;
  5.  
  6. package abstract class AbstractFallbackPackageSupplier : PackageSupplier
  7. {
  8. protected import core.time : minutes;
  9. protected import std.datetime : Clock, SysTime;
  10.  
  11. static struct Pair { PackageSupplier ps; SysTime failTime; }
  12. protected Pair[] m_suppliers;
  13.  
  14. this(PackageSupplier[] suppliers)
  15. {
  16. assert(suppliers.length);
  17. m_suppliers.length = suppliers.length;
  18. foreach (i, ps; suppliers)
  19. m_suppliers[i].ps = ps;
  20. }
  21.  
  22. override @property string description()
  23. {
  24. import std.algorithm.iteration : map;
  25. import std.format : format;
  26. return format("%s (fallbacks %-(%s, %))", m_suppliers[0].ps.description,
  27. m_suppliers[1 .. $].map!(pair => pair.ps.description));
  28. }
  29.  
  30. // Workaround https://issues.dlang.org/show_bug.cgi?id=2525
  31. abstract override Version[] getVersions(string package_id);
  32. abstract override void fetchPackage(NativePath path, string package_id, Dependency dep, bool pre_release);
  33. abstract override Json fetchPackageRecipe(string package_id, Dependency dep, bool pre_release);
  34. abstract override SearchResult[] searchPackages(string query);
  35. }
  36.  
  37.  
  38. /**
  39. Combines two package suppliers and uses the second as fallback to handle failures.
  40.  
  41. Assumes that both registries serve the same packages (--mirror).
  42. */
  43. package(dub) alias FallbackPackageSupplier = AutoImplement!(AbstractFallbackPackageSupplier, fallback);
  44.  
  45. private template fallback(T, alias func)
  46. {
  47. import std.format : format;
  48. enum fallback = q{
  49. import dub.internal.vibecompat.core.log : logDiagnostic;
  50.  
  51. Exception firstEx;
  52. try
  53. return m_suppliers[0].ps.%1$s(args);
  54. catch (Exception e)
  55. {
  56. logDiagnostic("Package supplier %%s failed with '%%s', trying fallbacks.",
  57. m_suppliers[0].ps.description, e.msg);
  58. firstEx = e;
  59. }
  60.  
  61. immutable now = Clock.currTime;
  62. foreach (ref pair; m_suppliers[1 .. $])
  63. {
  64. if (pair.failTime > now - 10.minutes)
  65. continue;
  66. try
  67. {
  68. scope (success) logDiagnostic("Fallback %%s succeeded", pair.ps.description);
  69. return pair.ps.%1$s(args);
  70. }
  71. catch (Exception e)
  72. {
  73. pair.failTime = now;
  74. logDiagnostic("Fallback package supplier %%s failed with '%%s'.",
  75. pair.ps.description, e.msg);
  76. }
  77. }
  78. throw firstEx;
  79. }.format(__traits(identifier, func));
  80. }