data-met.lua / last modification: 2020-01-30 14:16
if not modules then modules = { } end modules ['data-met'] = {
    version   = 1.100,
    comment   = "companion to luat-lib.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

local type = type
local find = string.find
local addurlscheme, urlhashed = url.addscheme, url.hashed
local collapsepath, joinfile = file.collapsepath, file.join

local report_methods = logs.reporter("resolvers","methods")

local trace_locating = false
local trace_methods  = false

trackers.register("resolvers.locating", function(v) trace_methods = v end)
trackers.register("resolvers.methods",  function(v) trace_methods = v end)

local allocate   = utilities.storage.allocate
local resolvers  = resolvers

local registered = { }

local function splitmethod(filename) -- todo: filetype in specification
    if not filename then
        return {
            scheme   = "unknown",
            original = filename,
        }
    end
    if type(filename) == "table" then
        return filename -- already split
    end
    filename = collapsepath(filename,".") -- hm, we should keep ./ in some cases
    if not find(filename,"://",1,true) then
        return {
            scheme   = "file",
            path     = filename,
            original = filename,
            filename = filename,
        }
    end
    local specification = urlhashed(filename)
    if not specification.scheme or specification.scheme == "" then
        return {
            scheme   = "file",
            path     = filename,
            original = filename,
            filename = filename,
        }
    else
        return specification
    end
end

-- local function splitmethod(filename) -- todo: filetype in specification
--     if not filename then
--         return { scheme = "unknown", original = filename }
--     end
--     if type(filename) == "table" then
--         return filename -- already split
--     end
--     return urlhashed(filename)
-- end

resolvers.splitmethod = splitmethod -- bad name but ok

-- the second argument is always analyzed (saves time later on) and the original
-- gets passed as original but also as argument

local function methodhandler(what,first,...) -- filename can be nil or false
    local method = registered[what]
    if method then
        local how       = method.how
        local namespace = method.namespace
        if how == "uri" or how == "url" then
            local specification = splitmethod(first)
            local scheme        = specification.scheme
            local resolver      = namespace and namespace[scheme]
            if resolver then
                if trace_methods then
                    report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,scheme,first)
                end
                return resolver(specification,...)
            else
                resolver = namespace.default or namespace.file
                if resolver then
                    if trace_methods then
                        report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,"default",first)
                    end
                    return resolver(specification,...)
                elseif trace_methods then
                    report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,"unset")
                end
            end
        elseif how == "tag" then
            local resolver = namespace and namespace[first]
            if resolver then
                if trace_methods then
                    report_methods("resolving, method %a, how %a, tag %a",what,how,first)
                end
                return resolver(...)
            else
                resolver = namespace.default or namespace.file
                if resolver then
                    if trace_methods then
                        report_methods("resolving, method %a, how %a, tag %a",what,how,"default")
                    end
                    return resolver(...)
                elseif trace_methods then
                    report_methods("resolving, method %a, how %a, tag %a",what,how,"unset")
                end
            end
        end
    else
        report_methods("resolving, invalid method %a")
    end
end

resolvers.methodhandler = methodhandler

function resolvers.registermethod(name,namespace,how)
    registered[name] = {
        how       = how or "tag",
        namespace = namespace
    }
    namespace["byscheme"] = function(scheme,filename,...)
        if scheme == "file" then
            return methodhandler(name,filename,...)
        else
            return methodhandler(name,addurlscheme(filename,scheme),...)
        end
    end
end

local concatinators = allocate { notfound = joinfile        }  -- concatinate paths
local locators      = allocate { notfound = function() end  }  -- locate databases
local hashers       = allocate { notfound = function() end  }  -- load databases
local generators    = allocate { notfound = function() end  }  -- generate databases

resolvers.concatinators = concatinators
resolvers.locators      = locators
resolvers.hashers       = hashers
resolvers.generators    = generators

local registermethod = resolvers.registermethod

registermethod("concatinators",concatinators,"tag")
registermethod("locators",     locators,     "uri")
registermethod("hashers",      hashers,      "uri")
registermethod("generators",   generators,   "uri")