summaryrefslogtreecommitdiff
path: root/lib/gitlab/ci/build/artifacts/metadata/entry.rb
blob: 22941d48edfce474cd1d54ab0f15bb76356596b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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 nil 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

            def total_size
              descendant_pattern = %r{^#{Regexp.escape(@path.to_s)}}
              entries.sum do |path, entry|
                (entry[:size] if path =~ descendant_pattern).to_i
              end
            end

            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