Newer
Older
dub_jkp / source / dub / recipe / selection.d
  1. /**
  2. * Contains type definition for `dub.selections.json`
  3. */
  4. module dub.recipe.selection;
  5.  
  6. import dub.dependency;
  7. import dub.internal.vibecompat.core.file : NativePath;
  8.  
  9. import dub.internal.configy.Attributes;
  10.  
  11. import std.exception;
  12.  
  13. public struct Selected
  14. {
  15. /// The current version of the file format
  16. public uint fileVersion;
  17.  
  18. /// The selected package and their matching versions
  19. public SelectedDependency[string] versions;
  20. }
  21.  
  22.  
  23. /// Wrapper around `SelectedDependency` to do deserialization but still provide
  24. /// a `Dependency` object to client code.
  25. private struct SelectedDependency
  26. {
  27. public Dependency actual;
  28. alias actual this;
  29.  
  30. /// Constructor, used in `fromYAML`
  31. public this (inout(Dependency) dep) inout @safe pure nothrow @nogc
  32. {
  33. this.actual = dep;
  34. }
  35.  
  36. /// Allow external code to assign to this object as if it was a `Dependency`
  37. public ref SelectedDependency opAssign (Dependency dep) return pure nothrow @nogc
  38. {
  39. this.actual = dep;
  40. return this;
  41. }
  42.  
  43. /// Read a `Dependency` from the config file - Required to support both short and long form
  44. static SelectedDependency fromYAML (scope ConfigParser!SelectedDependency p)
  45. {
  46. import dub.internal.dyaml.node;
  47.  
  48. if (p.node.nodeID == NodeID.scalar)
  49. return SelectedDependency(Dependency(Version(p.node.as!string)));
  50.  
  51. auto d = p.parseAs!YAMLFormat;
  52. if (d.path.length)
  53. return SelectedDependency(Dependency(NativePath(d.path)));
  54. else
  55. {
  56. assert(d.version_.length);
  57. if (d.repository.length)
  58. return SelectedDependency(Dependency(Repository(d.repository, d.version_)));
  59. return SelectedDependency(Dependency(Version(d.version_)));
  60. }
  61. }
  62.  
  63. /// In-file representation of a dependency as permitted in `dub.selections.json`
  64. private struct YAMLFormat
  65. {
  66. @Optional @Name("version") string version_;
  67. @Optional string path;
  68. @Optional string repository;
  69.  
  70. public void validate () const scope @safe pure
  71. {
  72. enforce(this.version_.length || this.path.length || this.repository.length,
  73. "Need to provide a version string, or an object with one of the following fields: `version`, `path`, or `repository`");
  74. enforce(!this.path.length || !this.repository.length,
  75. "Cannot provide a `path` dependency if a repository dependency is used");
  76. enforce(!this.path.length || !this.version_.length,
  77. "Cannot provide a `path` dependency if a `version` dependency is used");
  78. enforce(!this.repository.length || this.version_.length,
  79. "Cannot provide a `repository` dependency without a `version`");
  80. }
  81. }
  82. }
  83.  
  84. // Ensure we can read all type of dependencies
  85. unittest
  86. {
  87. import dub.internal.configy.Read : parseConfigString;
  88. import dub.internal.vibecompat.core.file : NativePath;
  89.  
  90. immutable string content = `{
  91. "fileVersion": 1,
  92. "versions": {
  93. "simple": "1.5.6",
  94. "branch": "~master",
  95. "branch2": "~main",
  96. "path": { "path": "../some/where" },
  97. "repository": { "repository": "git+https://github.com/dlang/dub", "version": "123456123456123456" }
  98. }
  99. }`;
  100.  
  101. auto s = parseConfigString!Selected(content, "/dev/null");
  102. assert(s.fileVersion == 1);
  103. assert(s.versions.length == 5);
  104. assert(s.versions["simple"] == Dependency(Version("1.5.6")));
  105. assert(s.versions["branch"] == Dependency(Version("~master")));
  106. assert(s.versions["branch2"] == Dependency(Version("~main")));
  107. assert(s.versions["path"] == Dependency(NativePath("../some/where")));
  108. assert(s.versions["repository"] == Dependency(Repository("git+https://github.com/dlang/dub", "123456123456123456")));
  109. }