BigW Consortium Gitlab

entry.rb 2.99 KB
Newer Older
1 2 3 4
module Gitlab
  module Ci::Build::Artifacts
    class Metadata
      ##
5
      # Class that represents an entry (path and metadata) to a file or
6 7 8 9 10
      # 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.
      #
11 12
      # This class is working only with UTF-8 encoded paths.
      #
13
      class Entry
14
        attr_reader :path, :entries
15 16
        attr_accessor :name

17 18
        def initialize(path, entries)
          @path = path.dup.force_encoding('UTF-8')
19
          @entries = entries
20 21 22 23

          if path.include?("\0")
            raise ArgumentError, 'Path contains zero byte character!'
          end
24 25 26 27

          unless path.valid_encoding?
            raise ArgumentError, 'Path contains non-UTF-8 byte sequence!'
          end
28 29 30
        end

        def directory?
31
          blank_node? || @path.end_with?('/')
32 33 34 35 36 37 38 39 40 41 42 43
        end

        def file?
          !directory?
        end

        def has_parent?
          nodes > 0
        end

        def parent
          return nil unless has_parent?
44
          self.class.new(@path.chomp(basename), @entries)
45 46 47
        end

        def basename
48
          (directory? && !blank_node?) ? name + '/' : name
49 50 51
        end

        def name
52
          @name || @path.split('/').last.to_s
53 54 55 56 57 58
        end

        def children
          return [] unless directory?
          return @children if @children

59
          child_pattern = %r{^#{Regexp.escape(@path)}[^/]+/?$}
60
          @children = select_entries { |path| path =~ child_pattern }
61 62
        end

63
        def directories(opts = {})
64
          return [] unless directory?
65 66
          dirs = children.select(&:directory?)
          return dirs unless has_parent? && opts[:parent]
67 68 69

          dotted_parent = parent
          dotted_parent.name = '..'
70
          dirs.prepend(dotted_parent)
71 72 73 74 75 76 77 78
        end

        def files
          return [] unless directory?
          children.select(&:file?)
        end

        def metadata
79
          @entries[@path] || {}
80 81 82 83 84 85
        end

        def nodes
          @path.count('/') + (file? ? 1 : 0)
        end

86 87 88 89
        def blank_node?
          @path.empty? # "" is considered to be './'
        end

90
        def exists?
91
          blank_node? || @entries.include?(@path)
92 93
        end

94 95
        def empty?
          children.empty?
96 97
        end

98 99 100 101 102 103 104
        def total_size
          descendant_pattern = %r{^#{Regexp.escape(@path)}}
          entries.sum do |path, entry|
            (entry[:size] if path =~ descendant_pattern).to_i
          end
        end

105 106 107 108 109
        def to_s
          @path
        end

        def ==(other)
110
          @path == other.path && @entries == other.entries
111 112 113 114 115 116 117 118
        end

        def inspect
          "#{self.class.name}: #{@path}"
        end

        private

119
        def select_entries
120
          selected = @entries.select { |path, _metadata| yield path }
121
          selected.map { |path, _metadata| self.class.new(path, @entries) }
122 123 124 125 126
        end
      end
    end
  end
end