1# ====================================================================
2#    Licensed to the Apache Software Foundation (ASF) under one
3#    or more contributor license agreements.  See the NOTICE file
4#    distributed with this work for additional information
5#    regarding copyright ownership.  The ASF licenses this file
6#    to you under the Apache License, Version 2.0 (the
7#    "License"); you may not use this file except in compliance
8#    with the License.  You may obtain a copy of the License at
9#
10#      http://www.apache.org/licenses/LICENSE-2.0
11#
12#    Unless required by applicable law or agreed to in writing,
13#    software distributed under the License is distributed on an
14#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15#    KIND, either express or implied.  See the License for the
16#    specific language governing permissions and limitations
17#    under the License.
18# ====================================================================
19
20require "English"
21require "tempfile"
22require "svn/error"
23require "svn/util"
24require "svn/core"
25require "svn/repos"
26require "svn/ext/ra"
27
28module Svn
29  module Ra
30    Util.set_constants(Ext::Ra, self)
31    Util.set_methods(Ext::Ra, self)
32
33    @@ra_pool = Svn::Core::Pool.new
34    Ra.initialize(@@ra_pool)
35
36    class << self
37      def modules
38        print_modules("")
39      end
40    end
41
42    Session = SWIG::TYPE_p_svn_ra_session_t
43
44    class Session
45      class << self
46        def open(url, config=nil, callbacks=nil)
47          pool = Core::Pool.new
48          session = Ra.open2(url, callbacks, config || Svn::Core::Config.get, pool)
49          session.instance_variable_set(:@pool, pool)
50          return session unless block_given?
51          begin
52            yield session
53          ensure
54            session.close
55          end
56        end
57      end
58
59      def close
60        @pool.destroy
61      end
62
63      def latest_revnum
64        Ra.get_latest_revnum(self)
65      end
66      alias latest_revision latest_revnum
67
68      def dated_revision(time)
69        Ra.get_dated_revision(self, time.to_apr_time)
70      end
71
72      def set_prop(name, value, rev=nil)
73        Ra.change_rev_prop(self, rev || latest_revnum, name, value)
74      end
75
76      def []=(name, *args)
77        value = args.pop
78        set_prop(name, value, *args)
79        value
80      end
81
82      def proplist(rev=nil)
83        Ra.rev_proplist(self, rev || latest_revnum)
84      end
85      alias properties proplist
86
87      def prop(name, rev=nil)
88        Ra.rev_prop(self, rev || latest_revnum, name)
89      end
90      alias [] prop
91
92      def commit_editor(log_msg, lock_tokens={}, keep_lock=false)
93        callback = Proc.new do |new_revision, date, author|
94          yield(new_revision, date, author)
95        end
96        editor, editor_baton = Ra.get_commit_editor(self, log_msg, callback,
97                                                    lock_tokens, keep_lock)
98        editor.baton = editor_baton
99        [editor, editor_baton]
100      end
101
102      def commit_editor2(log_msg_or_rev_props, lock_tokens={},
103                         keep_lock=false, &callback)
104        if log_msg_or_rev_props.is_a?(Hash)
105          rev_props = log_msg_or_rev_props
106        else
107          rev_props = {Svn::Core::PROP_REVISION_LOG => log_msg_or_rev_props}
108        end
109        editor, editor_baton = Ra.get_commit_editor3(self, rev_props, callback,
110                                                     lock_tokens, keep_lock)
111        editor.baton = editor_baton
112        editor
113      end
114
115      def file(path, rev=nil)
116        output = StringIO.new
117        rev ||= latest_revnum
118        fetched_rev, props = Ra.get_file(self, path, rev, output)
119        output.rewind
120        props_filter(props)
121        [output.read, props]
122      end
123
124      def dir(path, rev=nil, fields=nil)
125        rev ||= latest_revnum
126        fields ||= Core::DIRENT_ALL
127        entries, fetched_rev, props = Ra.get_dir2(self, path, rev, fields)
128        props_filter(props)
129        [entries, props]
130      end
131
132      def update(revision_to_update_to, update_target,
133                 editor, editor_baton, depth=nil, &block)
134        editor.baton = editor_baton
135        update2(revision_to_update_to, update_target,
136                editor, depth, &block)
137      end
138
139      def update2(revision_to_update_to, update_target, editor, depth=nil,
140                  send_copyfrom_args=nil)
141        reporter, reporter_baton = Ra.do_update2(self, revision_to_update_to,
142                                                 update_target, depth,
143                                                 send_copyfrom_args, editor)
144        reporter.baton = reporter_baton
145        if block_given?
146          yield(reporter)
147          reporter.finish_report
148          nil
149        else
150          reporter
151        end
152      end
153
154      def switch(revision_to_switch_to, switch_target, switch_url,
155                 editor, editor_baton, depth=nil, &block)
156        editor.baton = editor_baton
157        switch2(revision_to_switch_to, switch_target, switch_url,
158                editor, depth, &block)
159      end
160
161      def switch2(revision_to_switch_to, switch_target, switch_url,
162                  editor, depth=nil)
163        reporter, reporter_baton = Ra.do_switch2(self, revision_to_switch_to,
164                                                 switch_target, depth,
165                                                 switch_url, editor)
166        reporter.baton = reporter_baton
167        if block_given?
168          yield(reporter)
169          reporter.finish_report
170          nil
171        else
172          reporter
173        end
174      end
175
176      def status(revision, status_target, editor, editor_baton,
177                 recurse=true, &block)
178        editor.baton = editor_baton
179        status2(revision, status_target, editor, recurse, &block)
180      end
181
182      def status2(revision, status_target, editor, recurse=true)
183        reporter, reporter_baton = Ra.do_status2(self, status_target,
184                                                 revision, recurse, editor)
185        reporter.baton = reporter_baton
186        if block_given?
187          yield(reporter)
188          reporter.finish_report
189          nil
190        else
191          reporter
192        end
193      end
194
195      def diff(rev, target, versus_url, editor,
196               depth=nil, ignore_ancestry=true, text_deltas=true)
197        args = [self, rev, target, depth, ignore_ancestry,
198                text_deltas, versus_url, editor]
199        reporter, baton = Ra.do_diff3(*args)
200        reporter.baton = baton
201        reporter
202      end
203
204      def log(paths, start_rev, end_rev, limit,
205              discover_changed_paths=true,
206              strict_node_history=false)
207        paths = [paths] unless paths.is_a?(Array)
208        receiver = Proc.new do |changed_paths, revision, author, date, message|
209          yield(changed_paths, revision, author, date, message)
210        end
211        Ra.get_log(self, paths, start_rev, end_rev, limit,
212                   discover_changed_paths, strict_node_history,
213                   receiver)
214      end
215
216      def check_path(path, rev=nil)
217        Ra.check_path(self, path, rev || latest_revnum)
218      end
219
220      def stat(path, rev=nil)
221        Ra.stat(self, path, rev || latest_revnum)
222      end
223
224      def uuid
225        Ra.get_uuid(self)
226      end
227
228      def repos_root
229        Ra.get_repos_root(self)
230      end
231
232      def locations(path, location_revisions, peg_revision=nil)
233        peg_revision ||= latest_revnum
234        Ra.get_locations(self, path, peg_revision, location_revisions)
235      end
236
237      def file_revs(path, start_rev, end_rev=nil)
238        args = [path, start_rev, end_rev]
239        if block_given?
240          revs = file_revs2(*args) do |path, rev, rev_props, prop_diffs|
241            yield(path, rev, rev_props, Util.hash_to_prop_array(prop_diffs))
242          end
243        else
244          revs = file_revs2(*args)
245        end
246        revs.collect do |path, rev, rev_props, prop_diffs|
247          [path, rev, rev_props, Util.hash_to_prop_array(prop_diffs)]
248        end
249      end
250
251      def file_revs2(path, start_rev, end_rev=nil)
252        end_rev ||= latest_revnum
253        revs = []
254        handler = Proc.new do |path, rev, rev_props, prop_diffs|
255          revs << [path, rev, rev_props, prop_diffs]
256          yield(path, rev, rev_props, prop_diffs) if block_given?
257        end
258        Ra.get_file_revs(self, path, start_rev, end_rev, handler)
259        revs
260      end
261
262      def lock(path_revs, comment=nil, steal_lock=false)
263        lock_func = Proc.new do |path, do_lock, lock, ra_err|
264          yield(path, do_lock, lock, ra_err)
265        end
266        Ra.lock(self, path_revs, comment, steal_lock, lock_func)
267      end
268
269      def unlock(path_tokens, break_lock=false, &lock_func)
270        Ra.unlock(self, path_tokens, break_lock, lock_func)
271      end
272
273      def get_lock(path)
274        Ra.get_lock(self, path)
275      end
276
277      def get_locks(path)
278        Ra.get_locks(self, path)
279      end
280
281      def replay(rev, low_water_mark, editor, send_deltas=true)
282        Ra.replay(self, rev, low_water_mark, send_deltas, editor)
283      end
284
285      def reparent(url)
286        Ra.reparent(self, url)
287      end
288
289      def mergeinfo(paths, revision=nil, inherit=nil, include_descendants=false)
290        paths = [paths] unless paths.is_a?(Array)
291        revision ||= Svn::Core::INVALID_REVNUM
292        info = Ra.get_mergeinfo(self, paths, revision, inherit,
293                                include_descendants)
294        unless info.nil?
295          info.each_key do |key|
296            info[key] = Core::MergeInfo.new(info[key])
297          end
298        end
299        info
300      end
301
302      private
303      def props_filter(props)
304        date_str = props[Svn::Core::PROP_ENTRY_COMMITTED_DATE]
305        if date_str
306          date = Time.parse_svn_format(date_str)
307          props[Svn::Core::PROP_ENTRY_COMMITTED_DATE] = date
308        end
309        props
310      end
311    end
312
313    class Reporter3
314      attr_accessor :baton
315      def set_path(path, revision, depth=nil, start_empty=true,
316                   lock_token=nil)
317        Ra.reporter3_invoke_set_path(self, @baton, path, revision,
318                                     depth, start_empty, lock_token)
319      end
320
321      def delete_path(path)
322        Ra.reporter3_invoke_delete_path(self, @baton, path)
323      end
324
325      def link_path(path, url, revision, depth=nil,
326                    start_empty=true, lock_token=nil)
327        Ra.reporter3_invoke_link_path(self, @baton, path, url,
328                                      revision, depth, start_empty, lock_token)
329      end
330
331      def finish_report
332        Ra.reporter3_invoke_finish_report(self, @baton)
333      end
334
335      def abort_report
336        Ra.reporter3_invoke_abort_report(self, @baton)
337      end
338    end
339
340    remove_const(:Callbacks)
341    class Callbacks
342      include Core::Authenticatable
343
344      def initialize(auth_baton=nil)
345        self.auth_baton = auth_baton || Core::AuthBaton.new
346      end
347
348      def open_tmp_file
349        tmp = Tempfile.new("Svn::Ra")
350        path = tmp.path
351        tmp.close(true)
352        path
353      end
354
355      def get_wc_prop(relpath, name)
356        nil
357      end
358
359      def set_wc_prop(path, name, value)
360      end
361
362      def push_wc_prop(path, name, value)
363      end
364
365      def invalidate_wc_props(path, name)
366      end
367
368      def progress_func(progress, total)
369      end
370    end
371  end
372end
373