gitlab-org--gitlab-foss/lib/gitlab/ci/build/artifacts/metadata/entry.rb

137 lines
3.4 KiB
Ruby

# frozen_string_literal: true
module Gitlab
module Ci
module Build
module Artifacts
class Metadata
##
# Class that represents an entry (path and metadata) to a file or
# directory in GitLab CI Build Artifacts binary file / archive
#
# This is IO-operations safe class, that does similar job to
# Ruby's Pathname but without the risk of accessing filesystem.
#
# This class is working only with UTF-8 encoded paths.
#
class Entry
attr_reader :entries
attr_accessor :name
def initialize(path, entries)
@entries = entries
@path = Artifacts::Path.new(path)
end
delegate :empty?, to: :children
def directory?
blank_node? || @path.directory?
end
def file?
!directory?
end
def blob
return unless file?
@blob ||= Blob.decorate(::Ci::ArtifactBlob.new(self), nil)
end
def has_parent?
nodes > 0
end
def parent
return unless has_parent?
self.class.new(@path.to_s.chomp(basename), @entries)
end
def basename
directory? && !blank_node? ? name + '/' : name
end
def name
@name || @path.name
end
def children
return [] unless directory?
return @children if @children
child_pattern = %r{^#{Regexp.escape(@path.to_s)}[^/]+/?$}
@children = select_entries { |path| path =~ child_pattern }
end
def directories(opts = {})
return [] unless directory?
dirs = children.select(&:directory?)
return dirs unless has_parent? && opts[:parent]
dotted_parent = parent
dotted_parent.name = '..'
dirs.prepend(dotted_parent)
end
def files
return [] unless directory?
children.select(&:file?)
end
def metadata
@entries[@path.to_s] || {}
end
def nodes
@path.nodes + (file? ? 1 : 0)
end
def blank_node?
@path.to_s.empty? # "" is considered to be './'
end
def exists?
blank_node? || @entries.include?(@path.to_s)
end
# rubocop: disable CodeReuse/ActiveRecord
def total_size
descendant_pattern = /^#{Regexp.escape(@path.to_s)}/
entries.sum do |path, entry|
(entry[:size] if path =~ descendant_pattern).to_i
end
end
# rubocop: enable CodeReuse/ActiveRecord
def path
@path.to_s
end
def to_s
@path.to_s
end
def ==(other)
path == other.path && @entries == other.entries
end
def inspect
"#{self.class.name}: #{self}"
end
private
def select_entries
selected = @entries.select { |path, _metadata| yield path }
selected.map { |path, _metadata| self.class.new(path, @entries) }
end
end
end
end
end
end
end