class NSWTopo::Archive

def self.open(out_path: nil, in_path: nil, &block)

def self.open(out_path: nil, in_path: nil, &block)
  buffer, reader = StringIO.new, in_path ? Zlib::GzipReader : StringIO
  reader.open(*in_path) do |input|
    begin
      version = Version[input.comment]
      raise "map file too old: created with nswtopo %s, minimum %s required" % [version, MIN_VERSION] unless version >= MIN_VERSION
      raise "nswtopo too old: map file created with nswtopo %s, this version %s" % [version, VERSION] unless version <= VERSION
    rescue Version::Error
      raise "unrecognised map file: %s" % in_path
    end if in_path && false != Config["versioning"]
    Gem::Package::TarReader.new(input) do |tar|
      archive = new(tar).tap(&block)
      archive.each do |entry|
        buffer.write entry.header
        buffer.write entry.read
        buffer.write ?\0 while buffer.pos % 512 > 0
      end if archive.changed?
    end
  end
  Dir.mktmppath do |temp_dir|
    log_update "nswtopo: saving map..."
    temp_path = temp_dir / "temp.tgz"
    Zlib::GzipWriter.open temp_path, Config["zlib-level"] || Zlib::BEST_SPEED do |gzip|
      gzip.comment = VERSION.creator_string
      gzip.write buffer.string
    rescue Interrupt
      log_update "nswtopo: interrupted, please wait..."
      raise
    end
    safely "saving map file, please wait..." do
      FileUtils.cp temp_path, out_path
    rescue SystemCallError
      raise "couldn't save #{out_path}"
    end
    log_success "map saved"
  end if out_path && buffer.size.nonzero?
rescue Zlib::GzipFile::Error
  raise Invalid
end

def changed?

def changed?
  @entries.any?
end

def delete(filename)

def delete(filename)
  find do |entry|
    entry.full_name == filename
  end&.tap do
    @entries[filename] = nil
  end
end

def each(&block)

def each(&block)
  @tar.rewind
  @tar.each do |entry|
    yield entry unless @entries.key? entry.full_name
  end
  @entries.each do |filename, entry|
    yield entry.tap(&:rewind) if entry
  end
end

def initialize(tar)

def initialize(tar)
  @tar, @entries = tar, Hash[]
end

def mtime(filename)

def mtime(filename)
  find do |entry|
    entry.full_name == filename
  end&.then do |entry|
    Time.at entry.header.mtime
  end
end

def read(filename)

def read(filename)
  find do |entry|
    entry.full_name == filename
  end&.read
end

def uptodate?(depender, *dependees)

def uptodate?(depender, *dependees)
  return unless mtime(depender)
  dependees.all? do |dependee|
    mtimes = [depender, dependee].map(&method(:mtime))
    mtimes.all? && mtimes.inject(&:>=)
  end
end

def write(filename, content)

def write(filename, content)
  io = StringIO.new content
  header = Gem::Package::TarHeader.new name: filename, size: io.size, prefix: "", mode: 0o0644, mtime: Time.now
  @entries[filename] = Gem::Package::TarReader::Entry.new header, io
end