Add ContentType helper for callbacks
authorMichael Andres <ma@suse.de>
Fri, 25 Jul 2014 10:49:21 +0000 (12:49 +0200)
committerMichael Andres <ma@suse.de>
Fri, 25 Jul 2014 10:49:21 +0000 (12:49 +0200)
tests/zypp/CMakeLists.txt
tests/zypp/ContentType_test.cc [new file with mode: 0644]
zypp/CMakeLists.txt
zypp/ContentType.h [new file with mode: 0644]

index 0655d1a38a3366fce890ad30e268ca29eae300ff..acafee6fc5337954367d952e4ebda2a44a5035fa 100644 (file)
@@ -9,6 +9,7 @@ ADD_TESTS(
   Arch
   Capabilities
   CheckSum
+  ContentType
   CpeId
   Date
   Dup
diff --git a/tests/zypp/ContentType_test.cc b/tests/zypp/ContentType_test.cc
new file mode 100644 (file)
index 0000000..97346e6
--- /dev/null
@@ -0,0 +1,65 @@
+#include <boost/test/auto_unit_test.hpp>
+#include <set>
+#include "zypp/ContentType.h"
+
+using std::cout;
+using std::endl;
+
+using zypp::ContentType;
+
+BOOST_AUTO_TEST_CASE(contenttype_default)
+{
+  ContentType v;
+  BOOST_CHECK( !v );
+  BOOST_CHECK( v.empty() );
+  BOOST_CHECK( v.emptyType() );
+  BOOST_CHECK( v.emptySubtype() );
+
+  ContentType w( "/" );
+  BOOST_CHECK_EQUAL( v == w, true );
+  BOOST_CHECK_EQUAL( v != w, false );
+  BOOST_CHECK_EQUAL( v <  w, false );
+  BOOST_CHECK_EQUAL( v <= w, true );
+  BOOST_CHECK_EQUAL( v >  w, false );
+  BOOST_CHECK_EQUAL( v >= w, true );
+
+  BOOST_CHECK_EQUAL( v.asString(), "" );
+}
+
+BOOST_AUTO_TEST_CASE(contenttype_val)
+{
+  BOOST_CHECK_THROW( ContentType( " " ), std::invalid_argument );
+
+  BOOST_CHECK_THROW( ContentType( "//" ), std::invalid_argument );
+  BOOST_CHECK_THROW( ContentType( "/ " ), std::invalid_argument );
+
+  BOOST_CHECK_THROW( ContentType( "/", "a" ), std::invalid_argument );
+  BOOST_CHECK_THROW( ContentType( "a", "/" ), std::invalid_argument );
+
+  BOOST_CHECK_THROW( ContentType( " ", "a" ), std::invalid_argument );
+  BOOST_CHECK_THROW( ContentType( "a", " " ), std::invalid_argument );
+}
+
+BOOST_AUTO_TEST_CASE(contenttype_cmp)
+{
+  std::set<ContentType> c( {
+    ContentType( "" ),
+    ContentType( "/" ),                // == ""
+    ContentType( "a" ),
+    ContentType( "a/" ),       // == "a"
+    ContentType( "/a" ),
+    ContentType( "" , "a" ),   // == "/a"
+    ContentType( "a/b" ),
+    ContentType( "b/b" ),
+    ContentType( "b/c" )
+  });
+
+  std::set<ContentType>::const_iterator i = c.begin();
+  BOOST_CHECK_EQUAL( *(i++), ContentType() );
+  BOOST_CHECK_EQUAL( *(i++), ContentType( "", "a" ) );
+  BOOST_CHECK_EQUAL( *(i++), ContentType( "a", "" ) );
+  BOOST_CHECK_EQUAL( *(i++), ContentType( "a", "b" ) );
+  BOOST_CHECK_EQUAL( *(i++), ContentType( "b", "b" ) );
+  BOOST_CHECK_EQUAL( *(i++), ContentType( "b", "c" ) );
+  BOOST_CHECK( i == c.end() );
+}
\ No newline at end of file
index c8474fda8d48b36ddaebf453edc7f4158e5416d5..853bedff414fa4e17ca7f5e80e0017343ec911a6 100644 (file)
@@ -103,6 +103,7 @@ SET( zypp_HEADERS
   CapMatch.h
   Changelog.h
   CheckSum.h
+  ContentType.h
   CountryCode.h
   CpeId.h
   Date.h
diff --git a/zypp/ContentType.h b/zypp/ContentType.h
new file mode 100644 (file)
index 0000000..41a6b74
--- /dev/null
@@ -0,0 +1,144 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ContentType.h
+ */
+#ifndef ZYPP_CONTENTTYPE_H
+#define ZYPP_CONTENTTYPE_H
+
+#include <iosfwd>
+#include <string>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{
+  ///////////////////////////////////////////////////////////////////
+  /// \class ContentType
+  /// \brief Mime type like \c 'type/subtype' classification of content
+  ///
+  /// Used e.g. in \ref callback::UserData to describe the kind of
+  /// user data passed as \c void* to a callback. Neither type nor
+  /// subtype may contain a '/'.
+  ///////////////////////////////////////////////////////////////////
+  class ContentType
+  {
+  public:
+    /** Default ctor: empty */
+    ContentType()
+    {}
+
+    /** Ctor taking <tt>"type[/subtype]"</tt>
+     * \throws std::invalid_argument if string is malformed
+     */
+    explicit ContentType( const std::string & type_r )
+    {
+      std::string::size_type pos = type_r.find( "/" );
+      if ( pos == std::string::npos )
+      {
+       testAndSet( _type, type_r );
+      }
+      else
+      {
+       testAndSet( _type,    type_r.substr( 0, pos ) );
+       testAndSet( _subtype, type_r.substr( pos+1 ) );
+      }
+    }
+
+    /** Ctor taking type and subtype
+     * \throws std::invalid_argument if string is malformed
+     */
+    ContentType( const std::string & type_r, const std::string & subtype_r )
+    {
+      testAndSet( _type,    type_r );
+      testAndSet( _subtype, subtype_r );
+    }
+
+  public:
+    /** Get type */
+    const std::string & type() const
+    { return _type; }
+
+    /** Set type
+     * \throws std::invalid_argument if string is malformed
+     */
+    void type( const std::string & type_r )
+    { _type = type_r; }
+
+    /**  Get subtype */
+    const std::string & subtype() const
+    { return _subtype; }
+
+    /**  Set subtype
+     * \throws std::invalid_argument if string is malformed
+     */
+    void subtype( const std::string & subtype_r )
+    { _subtype = subtype_r; }
+
+  public:
+    /** Whether type and subtype are empty */
+    bool empty() const
+    { return emptyType() && emptySubtype(); }
+    /** Whether type is empty */
+    bool emptyType() const
+    { return _type.empty(); }
+    /** Whether subtype is empty */
+    bool emptySubtype() const
+    { return _subtype.empty(); }
+
+    /** Validate object in a boolean context: !empty */
+    explicit operator bool () const
+    { return !empty(); }
+
+    /** String representation <tt>"type[/subtype]"</tt> */
+    std::string asString() const
+    { std::string ret( type() ); if ( ! emptySubtype() ) { ret += "/"; ret += subtype(); } return ret; }
+
+  private:
+    void testAndSet( std::string & var_r, const std::string & val_r )
+    {
+      if ( val_r.find_first_of( "/ \t\r\n" ) != std::string::npos )
+       throw std::invalid_argument( "ContentType: illegal char in '" + val_r + "'" );
+      var_r = val_r;
+    }
+  private:
+    std::string        _type;
+    std::string        _subtype;
+  };
+
+  /** \relates ContentType Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const ContentType & obj )
+  { return str << obj.asString(); }
+
+  /** \relates ContentType */
+  inline bool operator==( const ContentType & lhs, const ContentType & rhs )
+  { return lhs.type() == rhs.type() && lhs.subtype() == rhs.subtype(); }
+
+  /** \relates ContentType */
+  inline bool operator!=( const ContentType & lhs, const ContentType & rhs )
+  { return !( lhs == rhs ); }
+
+  /** \relates ContentType */
+  inline bool operator<( const ContentType & lhs, const ContentType & rhs )
+  { int cmp = lhs.type().compare( rhs.type() ); return cmp < 0 || ( cmp == 0 && lhs.subtype() < rhs.subtype() ); }
+
+  /** \relates ContentType */
+  inline bool operator<=( const ContentType & lhs, const ContentType & rhs )
+  { return lhs < rhs || lhs == rhs; }
+
+  /** \relates ContentType */
+  inline bool operator>( const ContentType & lhs, const ContentType & rhs )
+  { return !( lhs <= rhs ); }
+
+  /** \relates ContentType */
+  inline bool operator>=( const ContentType & lhs, const ContentType & rhs )
+  { return !( lhs < rhs ); }
+
+
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_CONTENTTYPE_H