module HaveAPI::Fs::Components
  # Base class for all executables. Executables can be executed either by
  # writing `1` into them or by running them, as they contain a Ruby script.
  #
  # The Ruby script communicates with the file system using {RemoteControlFile}.
  # In short, it tells the file system to execute a component at a certain path,
  # which results in the same action as when `1` is written to the file.
  #
  # In both cases, the file system itself does the operation, the outer process
  # only signals it to do so and then waits for a reply.
  #
  # When the component is to be executed, method {#exec} is called.
  class Executable < File
    def executable?
      true
    end

    def writable?
      true
    end

    def read
      abs_path = ::File.join(context.mountpoint, path)

      "#!\#{RbConfig.ruby}\n#\n# This action can be executed either by running this file or by writing \"1\" to\n# it, e.g.:\n#   \n#   .\#{abs_path}\n#\n# or\n#\n#   echo 1 > \#{abs_path}\n#\nrequire 'yaml'\n\nf = ::File.open(\n    '\#{::File.join(context.mountpoint, '.remote_control')}',\n    'w+'\n)\nf.write(YAML.dump({\n    action: :execute,\n    path: '/\#{path}',\n}))\nf.write(\#{RemoteControlFile::MSG_DELIMITER.dump})\nf.flush\nf.seek(0)\n\nret = YAML.load(f.read)\nf.close\n\nunless ret[:status]\n  warn \"Action failed: \\\#{ret[:message]}\"\n  if ret[:errors]\n    ret[:errors].each do |k, v|\n      warn \"  \\\#{k}: \\\#{v.join('; ')}\"\n    end\n  end\nend\n\nexit(ret[:status])\n"
    end

    def write(str)
      exec if str.strip == '1'
    end

    def exec
      raise NotImplementedError
    end
  end
end