Merge pull request #23 from openSUSE/drop_package_manager
[platform/upstream/libzypp.git] / zypp / ProgressData.h
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/ProgressData.h
10  *
11 */
12 #ifndef ZYPP_PROGRESSDATA_H
13 #define ZYPP_PROGRESSDATA_H
14
15 #include <iosfwd>
16 #include <string>
17
18 #include "zypp/base/PtrTypes.h"
19 #include "zypp/base/Function.h"
20 #include "zypp/base/ProvideNumericId.h"
21
22 #include "zypp/Date.h"
23
24 ///////////////////////////////////////////////////////////////////
25 namespace zypp
26 { /////////////////////////////////////////////////////////////////
27
28   ///////////////////////////////////////////////////////////////////
29   //
30   //    CLASS NAME : ProgressData
31   //
32   /** Maintain <tt>[min,max]</tt> and counter <tt>(value)</tt> for progress counting.
33    *
34    * This class should provide everything the producer of progress data
35    * needs. As a convention, a zero sizes range indicates that you are just
36    * able to send <em>'still alive'</em> triggers.
37    *
38    * The counter should be updated in reasonable intervals. Don't mind whether
39    * the counter value actually increased or not. ProgressData will recognize
40    * your triggers and knows when to actually send notification to a consumer.
41    *
42    * Each ProgressData object provides a unique numeric id and you may assign
43    * it a name.
44    *
45    * \code
46    *      bool exampleReceiver( ProgressData::value_type v )
47    *      {
48    *        DBG << "got ->" << v << endl;
49    *        return( v <= 100 ); // Abort if ( v > 100 )
50    *      }
51    *
52    *      class Example
53    *      {
54    *        public:
55    *
56    *          Example( const ProgressData::ReceiverFnc & fnc_r = ProgressData::ReceiverFnc() )
57    *          : _fnc( fnc_r )
58    *          {}
59    *
60    *          void SendTo( const ProgressData::ReceiverFnc & fnc_r )
61    *          { _fnc = fnc_r; }
62    *
63    *        public:
64    *
65    *          void action()
66    *          {
67    *            ProgressData tics( 10 );    // Expect range 0 -> 10
68    *            tics.name( "test ticks" );  // Some arbitrary name
69    *            tics.sendTo( _fnc );        // Send reports to _fnc
70    *            tics.toMin();               // start sending min (0)
71    *
72    *            for ( int i = 0; i < 10; ++i )
73    *            {
74    *              if ( ! tics.set( i ) )
75    *                return; // user requested abort
76    *            }
77    *
78    *            tics.toMax(); // take care 100% are reported on success
79    *          }
80    *
81    *          void action2()
82    *          {
83    *            ProgressData tics;          // Just send 'still alive' messages
84    *            tics.name( "test ticks" );  // Some arbitrary name
85    *            tics.sendTo( _fnc );        // Send reports to _fnc
86    *            tics.toMin();               // start sending min (0)
87    *
88    *            for ( int i = 0; i < 10; ++i )
89    *            {
90    *              if ( ! tics.set( i ) )
91    *                return; // user requested abort
92    *            }
93    *
94    *            tics.toMax(); //
95    *          }
96    *
97    *        private:
98    *          ProgressData::ReceiverFnc _fnc;
99    *      };
100    * \endcode
101    * \code
102    *   Example t( exampleReceiver );
103    *   DBG << "Reporting %:" << endl;
104    *   t.action();
105    *   DBG << "Reporting 'still alive':" << endl;
106    *   t.action2();
107    * \endcode
108    * \code
109    * Reporting %:
110    * got ->0
111    * got ->10
112    * got ->20
113    * got ->30
114    * got ->40
115    * got ->50
116    * got ->60
117    * got ->70
118    * got ->80
119    * got ->90
120    * got ->100
121    * got ->100
122    * Reporting 'still alive':
123    * got ->0
124    * got ->9
125    * \endcode
126    *
127    * The different ammount of triggers is due to different rules for sending
128    * percent or 'still alive' messages.
129    *
130    * \todo Complete the progess sending.
131    * \todo Tell recipient whether percentage or keepalive is sent,
132    * the id and name might be helpfull, and maybe tell whether task
133    * is abortable or not; i.e extend the ReceiverFnc signature.
134    */
135   class ProgressData : public base::ProvideNumericId<ProgressData,unsigned>
136   {
137     public:
138       typedef long long value_type;
139       /** Most simple version of progress reporting
140        * The percentage in most cases. Sometimes just keepalive.
141        * \p sender ProgressData object who sends the progress info
142        * \p
143        */
144       typedef function<bool( const ProgressData & )> ReceiverFnc;
145
146     private:
147       enum State { INIT, RUN, END };
148
149       class Data
150       {
151         public:
152           Data( value_type min_r, value_type max_r, value_type val_r )
153           : _state( INIT ), _min( min_r ), _max( max_r ), _val( val_r )
154           , _last_val( 0 ), _last_send( 0 )
155           {}
156
157         public:
158           State       _state;
159           std::string _name;
160           value_type  _min;
161           value_type  _max;
162           value_type  _val;
163
164           ReceiverFnc _receiver;
165           value_type  _last_val;
166           Date        _last_send;
167
168         private:
169           /** clone for RWCOW_pointer */
170           friend Data * rwcowClone<Data>( const Data * rhs );
171           Data * clone() const { return new Data( *this ); }
172       };
173
174     public:
175       /** Ctor no range <tt>[0,0](0)</tt>. */
176       ProgressData()
177       : _d( new Data( 0, 0, 0 ) )
178       {}
179
180       /** Ctor <tt>[0,max](0)</tt>. */
181       ProgressData( value_type max_r )
182       : _d( new Data( 0, max_r, 0 ) )
183       {}
184
185       /** Ctor <tt>[min,max](min)</tt>. */
186       ProgressData( value_type min_r, value_type max_r )
187       : _d( new Data( min_r, max_r, min_r ) )
188       {}
189
190       /** Ctor <tt>[min,max](val)</tt>. */
191       ProgressData( value_type min_r, value_type max_r, value_type val_r )
192       : _d( new Data( min_r, max_r, val_r ) )
193       {}
194
195       ~ProgressData()
196       {
197         if ( _d->_state == RUN )
198         {
199           _d->_state = END;
200           report();
201         }
202       }
203
204     public:
205       /** Set new \c min value. */
206       void min( value_type min_r )
207       { _d->_min = min_r; }
208
209       /** Set new \c max value. */
210       void max( value_type max_r )
211       { _d->_max = max_r; }
212
213       /** Set no range <tt>[0,0]</tt>. */
214       void noRange()
215       { range( 0, 0 ); }
216
217       /** Set new <tt>[0,max]</tt>. */
218       void range( value_type max_r )
219       { range( 0, max_r ); }
220
221       /** Set new <tt>[min,max]</tt>. */
222       void range( value_type min_r, value_type max_r )
223       { min( min_r ); max( max_r ); }
224
225     public:
226       /** Set counter name. */
227       void name( const std::string & name_r )
228       { _d->_name = name_r; }
229
230       /** Set ReceiverFnc. */
231       void sendTo( const ReceiverFnc & fnc_r )
232       { _d->_receiver = fnc_r; }
233
234       /** Set no ReceiverFnc. */
235       void noSend()
236       { _d->_receiver = ReceiverFnc(); }
237
238     public:
239       /** \name Progress reporting.
240        *
241        * These methods may actually cause a progress report to be sent.
242        *
243        * All methods return \c bool, because a progress receiver may
244        * return \c false to indicate the desire to abort the pending
245        * action. The incident is logged, but it's finaly up to the caller
246        * to honor this.
247        */
248       //@{
249
250       /** Set new counter \c value. */
251       bool set( value_type val_r )
252       {
253         _d->_val = val_r;
254         return report();
255       }
256
257       /** Increment counter \c value (default by 1). */
258       bool incr( value_type val_r = 1 )
259       { return set( val() + val_r ); }
260
261       /** Decrement counter \c value (default by 1). */
262       bool decr( value_type val_r = 1 )
263       { return set( val() - val_r ); }
264
265       /** Set counter value to current \c min value. */
266       bool toMin()
267       { return set( min() ); }
268
269       /** Set counter value to current \c max value (unless no range). */
270       bool toMax()
271       { return set( hasRange() ? max() : val() ); }
272
273       /** Leave counter value unchanged (still alive). */
274       bool tick()
275       { return set( val() ); }
276
277       //@}
278
279     public:
280       /** \name Progress receiving.
281        */
282       //@{
283       /** @return Current \c min value. */
284       value_type min() const
285       { return _d->_min; }
286
287       /** @return Current \c max value. */
288       value_type max() const
289       { return _d->_max; }
290
291       /** @return Current counter \c value. */
292       value_type val() const
293       { return _d->_val; }
294
295       /** @return Whether <tt>[min,max]</tt> defines a nonempty range. */
296       bool hasRange() const
297       { return min() != max(); }
298
299       /** @return Whether \ref reportValue will return a percent value.
300        * Same as \ref hasRange.
301        *  \see \ref reportAlive
302        */
303       bool reportPercent() const
304       { return hasRange(); }
305
306       /** @return Whether \ref reportValue always returns -1, because we
307        * trigger 'still alive' messages. I.e. \ref hasrange is \c false.
308        * \see \ref reportPercent
309       */
310       bool reportAlive() const
311       { return ! hasRange(); }
312
313       /** @return Either a a percent value or -1.
314        * \see \ref reportPercent and \ref reportAlive.
315       */
316       value_type reportValue() const
317       { return hasRange() ? val() * 100 / ( max() - min() ) : -1; }
318
319       /** @return The counters name. */
320       const std::string & name() const
321       { return _d->_name; }
322
323       /** @return The ReceiverFnc. */
324       const ReceiverFnc & receiver() const
325       { return _d->_receiver; }
326
327       /** @return Retrun \c true if this the final report sent by the
328        *  ProgressData dtor.
329       */
330       bool finalReport() const
331       { return( _d->_state == END ); }
332
333       //@}
334
335     private:
336       /** Send report if necessary. */
337       bool report();
338
339       /** Pointer to data. */
340       RWCOW_pointer<Data> _d;
341   };
342   ///////////////////////////////////////////////////////////////////
343
344   /** \relates ProgressData Stream output */
345   std::ostream & operator<<( std::ostream & str, const ProgressData & obj );
346
347   ///////////////////////////////////////////////////////////////////
348
349   class InputStream;
350   /** \relates ProgressData Setup from \ref InputStream. */
351   ProgressData makeProgressData( const InputStream & input_r );
352
353   ///////////////////////////////////////////////////////////////////
354
355   /**
356    * \short Progress callback from another progress
357    *
358    * This class allows you to pass a progress callback to a
359    * subtask based on a current progress data, plus a weight
360    * value. Every progress reported by the subtask via
361    * this callback will be forwarded to the main progress
362    * data, with the corresponding weight.
363    *
364    * Example:
365    *
366    * \code
367    *
368    * // receiver for main task
369    * void task_receiver( ProgressData &progress );
370    *
371    * // subtask prototypes
372    * void do_subtask_one( ProgressData::ReceiverFnc &fnc );
373    * void do_subtask_two( ProgressData::ReceiverFnc &fnc );
374    *
375    * // main task
376    * ProgressData progress;
377    * //progress for subtask 1
378    * // which is 80%
379    * CombinedProgressData sub1(pd, 80);
380    * // the second is only 20%
381    * CombinedProgressData sub2(pd, 20);
382    * do_subtask_one( sub1 );
383    * do_subtask_two( sub2 );
384    *
385    * \endcode
386    */
387   class CombinedProgressData
388   {
389   public:
390     /**
391      * \short Ctor
392      *
393      * Creates a \ref ProgressData::ReceiverFnc
394      * from a \ref ProgressData object
395      *
396      * \param pd \ref ProgressData object
397      * \param weight Weight of the subtask
398      * relative to the main task range.
399      *
400      * If weight is 0, or \param pd only reports
401      * keepalives. then only ticks are sent.
402      *
403      */
404     CombinedProgressData( ProgressData &pd,
405                           ProgressData::value_type weight = 0 );
406
407     /**
408      * Implements the \ref ProgressData::ReceiverFnc
409      * callback interface
410      */
411     bool operator()( const ProgressData &progress );
412
413   private:
414     ProgressData::value_type _weight;
415     ProgressData::value_type _last_value;
416     ProgressData &_pd;
417   };
418
419   /////////////////////////////////////////////////////////////////
420 } // namespace zypp
421 ///////////////////////////////////////////////////////////////////
422 #endif // ZYPP_PROGRESSDATA_H