
--- Proxy module for filesystem and platform abstractions.
-- All code using "fs" code should require "luarocks.fs",
-- and not the various platform-specific implementations.
-- However, see the documentation of the implementation
-- for the API reference.

local pairs = pairs

local fs = {}
-- To avoid a loop when loading the other fs modules.
package.loaded["luarocks.fs"] = fs

local cfg = require("luarocks.core.cfg")

local pack = table.pack or function(...) return { n = select("#", ...), ... } end

math.randomseed(os.time())

local fs_is_verbose = false

do
   local old_popen, old_execute

   -- patch io.popen and os.execute to display commands in verbose mode
   function fs.verbose()
      fs_is_verbose = true

      if old_popen or old_execute then return end
      old_popen = io.popen
      -- luacheck: push globals io os
      io.popen = function(one, two)
         if two == nil then
            print("\nio.popen: ", one)
         else
            print("\nio.popen: ", one, "Mode:", two)
         end
         return old_popen(one, two)
      end

      old_execute = os.execute
      os.execute = function(cmd)
         -- redact api keys if present
         print("\nos.execute: ", (cmd:gsub("(/api/[^/]+/)([^/]+)/", function(cap, key) return cap.."<redacted>/" end)) )
         local a, b, c = old_execute(cmd)
         if type(a) == "boolean" then
            print((a and ".........." or "##########") .. ": " .. tostring(c) .. (b == "exit" and "" or " (" .. tostring(b) .. ")"))
         elseif type(a) == "number" then
            print(((a == 0) and ".........." or "##########") .. ": " .. tostring(a))
         end
         return a, b, c
      end
      -- luacheck: pop
   end
end

do
   local skip_verbose_wrap = {
      ["current_dir"] = true,
   }

   local function load_fns(module_name, inits)
      local ok, fs_table = pcall(require, module_name)
      if not ok or not type(fs_table) == "table" then
         return
      end

      for name, fn in pairs(fs_table) do
         if name ~= "init" and not fs[name] then
            if skip_verbose_wrap[name] then
               fs[name] = fn
            else
               fs[name] = function(...)
                  if fs_is_verbose then
                     local args = pack(...)
                     for i=1, args.n do
                        local arg = args[i]
                        local pok, v = pcall(string.format, "%q", arg)
                        args[i] = pok and v or tostring(arg)
                     end
                     print("fs." .. name .. "(" .. table.concat(args, ", ") .. ")")
                  end
                  return fn(...)
               end
            end
         end
      end
      if fs_table.init then
         table.insert(inits, fs_table.init)
      end
   end

   local function load_platform_fns(patt, inits)
      local each_platform = cfg.each_platform

      -- FIXME A quick hack for the experimental Windows build
      if os.getenv("LUAROCKS_CROSS_COMPILING") then
         each_platform = function()
            local i = 0
            local plats = { "linux", "unix" }
            return function()
               i = i + 1
               return plats[i]
            end
         end
      end

      for platform in each_platform("most-specific-first") do
         load_fns(patt:format(platform), inits)
      end
   end

   function fs.init()
      local inits = {}

      if fs.current_dir then
         -- unload luarocks fs so it can be reloaded using all modules
         -- providing extra functionality in the current package paths
         for k, _ in pairs(fs) do
            if k ~= "init" and k ~= "verbose" then
               fs[k] = nil
            end
         end
         for m, _ in pairs(package.loaded) do
            if m:match("luarocks%.fs%.") then
               package.loaded[m] = nil
            end
         end
      end

      -- Load platform-specific functions
      load_platform_fns("luarocks.fs.%s", inits)

      -- Load platform-independent pure-Lua functionality
      load_fns("luarocks.fs.lua", inits)

      -- Load platform-specific fallbacks for missing Lua modules
      load_platform_fns("luarocks.fs.%s.tools", inits)

      -- Load platform-independent external tool functionality
      load_fns("luarocks.fs.tools", inits)

      -- Run platform-specific initializations after everything is loaded
      for _, init in ipairs(inits) do
         init()
      end
   end
end

return fs
