M .rubocop.yml => .rubocop.yml +3 -0
@@ 67,6 67,9 @@ Style/StringLiteralsInInterpolation:
Style/SymbolArray:
EnforcedStyle: brackets
+Style/WordArray:
+ EnforcedStyle: brackets
+
Naming/UncommunicativeBlockParamName:
Enabled: false
M Gemfile => Gemfile +1 -0
@@ 5,3 5,4 @@ source "https://rubygems.org"
gem "cbor"
gem "promise.rb"
gem "value_semantics"
+gem "webmock"
M lib/dhall/ast.rb => lib/dhall/ast.rb +5 -1
@@ 572,7 572,7 @@ module Dhall
(uri.scheme == "https" ? Https : Http).new(
nil,
"#{uri.host}:#{uri.port}",
- uri.path.split(/\//)[1..-1],
+ *uri.path.split(/\//)[1..-1],
uri.query,
nil
)
@@ 636,6 636,10 @@ module Dhall
def pathname
Pathname.new("/").join(*path)
end
+
+ def to_uri(scheme, authority)
+ scheme.new(nil, authority, *path, nil, nil)
+ end
end
class RelativePath < Path
M lib/dhall/resolve.rb => lib/dhall/resolve.rb +69 -3
@@ 21,7 21,9 @@ module Dhall
ReadHttpSources = lambda do |sources|
sources.map do |source|
Promise.resolve(nil).then do
- Net::HTTP.get(source.uri)
+ r = Net::HTTP.get_response(source.uri)
+ raise ImportFailedException, source if r.code != "200"
+ r.body
end
end
end
@@ 32,6 34,50 @@ module Dhall
end
end
+ class ReadPathAndIPFSSources
+ def initialize(
+ path_reader: ReadPathSources,
+ http_reader: ReadHttpSources,
+ https_reader: http_reader,
+ public_gateway: "cloudflare-ipfs.com"
+ )
+ @path_reader = path_reader
+ @http_reader = http_reader
+ @https_reader = https_reader
+ @public_gateway = public_gateway
+ end
+
+ def call(sources)
+ @path_reader.call(sources).map.with_index do |promise, idx|
+ source = sources[idx]
+ if source.is_a?(Import::AbsolutePath) &&
+ ["ipfs", "ipns"].include?(source.path.first)
+ gateway_fallback(source, promise)
+ else
+ promise
+ end
+ end
+ end
+
+ def to_proc
+ method(:call).to_proc
+ end
+
+ protected
+
+ def gateway_fallback(source, promise)
+ promise.catch {
+ @http_reader.call([
+ source.to_uri(Import::Http, "localhost:8000")
+ ]).first
+ }.catch do
+ @https_reader.call([
+ source.to_uri(Import::Https, @public_gateway)
+ ]).first
+ end
+ end
+ end
+
class ResolutionSet
attr_reader :reader
@@ 66,7 112,7 @@ module Dhall
end
end
- class Default
+ class Standard
def initialize(
path_reader: ReadPathSources,
http_reader: ReadHttpSources,
@@ 111,7 157,27 @@ module Dhall
end
end
- class LocalOnly < Default
+ class Default < Standard
+ def initialize(
+ path_reader: ReadPathSources,
+ http_reader: ReadHttpSources,
+ https_reader: http_reader,
+ ipfs_public_gateway: "cloudflare-ipfs.com"
+ )
+ super(
+ 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
+ )
+ end
+ end
+
+ class LocalOnly < Standard
def initialize(path_reader: ReadPathSources)
super(
path_reader: path_reader,
M test/test_resolve.rb => test/test_resolve.rb +30 -0
@@ 1,6 1,7 @@
# frozen_string_literal: true
require "base64"
+require "webmock/minitest"
require "minitest/autorun"
require "dhall/resolve"
@@ 172,6 173,35 @@ class TestResolve < Minitest::Test
assert_equal Dhall::Variable["_"], expr.resolve(@resolver).sync
end
+ def test_ipfs
+ stub_request(:get, "http://localhost:8000/ipfs/TESTCID")
+ .to_return(status: 200, body: "\x00")
+
+ expr = Dhall::Import.new(
+ nil,
+ Dhall.method(:from_binary),
+ Dhall::Import::AbsolutePath.new("ipfs", "TESTCID")
+ )
+
+ assert_equal Dhall::Variable["_"], expr.resolve.sync
+ end
+
+ def test_ipfs_public_gateway
+ stub_request(:get, "http://localhost:8000/ipfs/TESTCID")
+ .to_return(status: 500)
+
+ stub_request(:get, "https://cloudflare-ipfs.com/ipfs/TESTCID")
+ .to_return(status: 200, body: "\x00")
+
+ expr = Dhall::Import.new(
+ nil,
+ Dhall.method(:from_binary),
+ Dhall::Import::AbsolutePath.new("ipfs", "TESTCID")
+ )
+
+ assert_equal Dhall::Variable["_"], expr.resolve.sync
+ end
+
# Sanity check that all expressions can pass through the resolver
Pathname.glob(TESTS + "**/*A.dhallb").each do |path|
test = path.relative_path_from(TESTS).to_s.sub(/A\.dhallb$/, "")