M lib/dhall/ast.rb => lib/dhall/ast.rb +32 -20
@@ 1225,13 1225,15 @@ module Dhall
end
def self.from_string(s)
- parts = s.to_s.split(/\//)
- if parts.first == ""
- AbsolutePath.new(*parts[1..-1])
- elsif parts.first == "~"
- RelativeToHomePath.new(*parts[1..-1])
+ prefix, *suffix = s.to_s.split(/\//)
+ if prefix == ""
+ AbsolutePath.new(*suffix)
+ elsif prefix == "~"
+ RelativeToHomePath.new(*suffix)
+ elsif prefix == ".."
+ RelativeToParentPath.new(*suffix)
else
- RelativePath.new(*parts)
+ RelativePath.new(prefix, *suffix)
end
end
@@ 1303,7 1305,7 @@ module Dhall
Pathname.new("~").join(*@path)
end
- def chain_onto(*)
+ def chain_onto(relative_to)
if relative_to.is_a?(URI)
raise ImportBannedException, "remote import cannot import #{self}"
end
@@ 1331,6 1333,8 @@ module Dhall
end
end
+ attr_reader :var
+
def initialize(var)
@var = var
end
@@ 1340,28 1344,27 @@ module Dhall
raise ImportBannedException, "remote import cannot import #{self}"
end
- real_path.chain_onto(relative_to)
+ self
+ end
+
+ def path
+ []
+ end
+
+ def with(path:)
+ Path.from_string(path.join("/"))
end
def canonical
- real_path.canonical
+ self
end
def real_path
- val = ENV.fetch(@var) do
- raise ImportFailedException, "No #{self}"
- end
- if val =~ /\Ahttps?:\/\//
- URI.from_uri(URI(val))
- else
- Path.from_string(val)
- end
+ self
end
def resolve(resolver)
- Promise.resolve(nil).then do
- real_path.resolve(resolver)
- end
+ resolver.resolve_environment(self)
end
def origin
@@ 1372,6 1375,15 @@ module Dhall
"env:#{as_json}"
end
+ def hash
+ @var.hash
+ end
+
+ def eql?(other)
+ other.is_a?(self.class) && other.var == var
+ end
+ alias eql? ==
+
def as_json
@var.gsub(/[\"\\\a\b\f\n\r\t\v]/) do |c|
"\\" + ESCAPES.find { |(_, v)| v == c }.first
M lib/dhall/resolve.rb => lib/dhall/resolve.rb +44 -14
@@ 20,6 20,16 @@ module Dhall
end
end
+ ReadEnvironmentSources = lambda do |sources|
+ sources.map do |source|
+ Promise.resolve(nil).then do
+ ENV.fetch(source.var) do
+ raise ImportFailedException, "No #{source}"
+ end
+ end
+ end
+ end
+
PreflightCORS = lambda do |source, parent_origin|
uri = source.uri
if parent_origin != "localhost" && parent_origin != source.origin
@@ 175,11 185,13 @@ module Dhall
def initialize(
path_reader: ReadPathSources,
http_reader: StandardReadHttpSources,
- https_reader: http_reader
+ https_reader: http_reader,
+ environment_reader: ReadEnvironmentSources
)
@path_resolutions = ResolutionSet.new(path_reader)
@http_resolutions = ResolutionSet.new(http_reader)
@https_resolutions = ResolutionSet.new(https_reader)
+ @env_resolutions = ResolutionSet.new(environment_reader)
@cache = {}
end
@@ 195,6 207,10 @@ module Dhall
@path_resolutions.register(path_source)
end
+ def resolve_environment(env_source)
+ @env_resolutions.register(env_source)
+ end
+
def resolve_http(http_source)
http_source.headers.resolve(
resolver: self,
@@ 220,6 236,7 @@ module Dhall
def finish!
[
@path_resolutions,
+ @env_resolutions,
@http_resolutions,
@https_resolutions
].each do |rset|
@@ 232,6 249,7 @@ module Dhall
dup.tap do |c|
c.instance_eval do
@path_resolutions = @path_resolutions.child(parent_source)
+ @env_resolutions = @env_resolutions.child(parent_source)
@http_resolutions = @http_resolutions.child(parent_source)
@https_resolutions = @https_resolutions.child(parent_source)
end
@@ 244,27 262,32 @@ module Dhall
path_reader: ReadPathSources,
http_reader: ReadHttpSources,
https_reader: http_reader,
+ environment_reader: ReadEnvironmentSources,
ipfs_public_gateway: "cloudflare-ipfs.com"
)
super(
- path_reader: ReadPathAndIPFSSources.new(
+ path_reader: ReadPathAndIPFSSources.new(
path_reader: path_reader,
http_reader: http_reader,
https_reader: https_reader,
public_gateway: ipfs_public_gateway
),
- http_reader: http_reader,
- https_reader: https_reader
+ http_reader: http_reader, https_reader: https_reader,
+ environment_reader: environment_reader
)
end
end
class LocalOnly < Standard
- def initialize(path_reader: ReadPathSources)
+ def initialize(
+ path_reader: ReadPathSources,
+ environment_reader: ReadEnvironmentSources
+ )
super(
- path_reader: path_reader,
- http_reader: RejectSources,
- https_reader: RejectSources
+ path_reader: path_reader,
+ environment_reader: environment_reader,
+ http_reader: RejectSources,
+ https_reader: RejectSources
)
end
end
@@ 272,9 295,10 @@ module Dhall
class None < Default
def initialize
super(
- path_reader: RejectSources,
- http_reader: RejectSources,
- https_reader: RejectSources
+ path_reader: RejectSources,
+ environment_reader: RejectSources,
+ http_reader: RejectSources,
+ https_reader: RejectSources
)
end
end
@@ 328,9 352,15 @@ module Dhall
class FallbackResolver < ExpressionResolver
register_for Operator::ImportFallback
- def resolve(**kwargs)
- ExpressionResolver.for(@expr.lhs).resolve(**kwargs).catch do
- ExpressionResolver.for(@expr.rhs).resolve(**kwargs)
+ def resolve(resolver:, relative_to:)
+ ExpressionResolver.for(@expr.lhs).resolve(
+ resolver: resolver,
+ relative_to: relative_to
+ ).catch do
+ @expr.rhs.resolve(
+ resolver: resolver.child(Import::MissingImport.new),
+ relative_to: relative_to
+ )
end
end
end
M test/test_resolve.rb => test/test_resolve.rb +39 -1
@@ 10,7 10,7 @@ class TestResolve < Minitest::Test
def setup
@relative_to = Dhall::Import::RelativePath.new
@resolver = Dhall::Resolvers::Default.new(
- path_reader: lambda do |sources|
+ path_reader: lambda do |sources|
sources.map do |source|
Promise.resolve(Base64.decode64({
"var" => "AA",
@@ 25,6 25,14 @@ class TestResolve < Minitest::Test
"headers" => "gwT2ggiiZmhlYWRlcoISYnRoZXZhbHVlghJidHY"
}.fetch(source.pathname.to_s)))
end
+ end,
+ environment_reader: lambda do |sources|
+ sources.map do |source|
+ Promise.resolve({
+ "NAT" => "1",
+ "PATH" => "./var"
+ }.fetch(source.var))
+ end
end
)
end
@@ 195,6 203,36 @@ class TestResolve < Minitest::Test
assert_equal Dhall::Variable["_"], subject(expr)
end
+ def test_env_natural
+ expr = Dhall::Import.new(
+ Dhall::Import::IntegrityCheck.new,
+ Dhall::Import::Expression,
+ Dhall::Import::EnvironmentVariable.new("NAT")
+ )
+
+ assert_equal Dhall::Natural.new(value: 1), subject(expr)
+ end
+
+ def test_env_as_text
+ expr = Dhall::Import.new(
+ Dhall::Import::IntegrityCheck.new,
+ Dhall::Import::Text,
+ Dhall::Import::EnvironmentVariable.new("NAT")
+ )
+
+ assert_equal Dhall::Text.new(value: "1"), subject(expr)
+ end
+
+ def test_env_relative
+ expr = Dhall::Import.new(
+ Dhall::Import::IntegrityCheck.new,
+ Dhall::Import::Expression,
+ Dhall::Import::EnvironmentVariable.new("PATH")
+ )
+
+ assert_equal Dhall::Variable["_"], subject(expr)
+ end
+
def test_ipfs
stub_request(:get, "http://localhost:8000/ipfs/TESTCID")
.to_return(status: 200, body: "\x00".b)
M test/test_resolvers.rb => test/test_resolvers.rb +3 -6
@@ 18,19 18,16 @@ class TestResolvers < Minitest::Test
assert_equal source, promise.sync
end
- def test_default_resolver_path_from_env
- ENV["__DHALL_IMPORT_TEST"] = "/dhall/common/x.dhall"
+ def test_default_resolver_env
resolver = Dhall::Resolvers::Default.new(
- path_reader: lambda do |sources|
+ environment_reader: lambda do |sources|
sources.map { |source| Promise.resolve(source) }
end
)
source = Dhall::Import::EnvironmentVariable.new("__DHALL_IMPORT_TEST")
promise = source.resolve(resolver)
resolver.finish!
-
- expected = Dhall::Import::AbsolutePath.new("dhall", "common", "x.dhall")
- assert_equal expected, promise.sync
+ assert_equal source, promise.sync
end
def test_default_resolver_http