filters: Add propertiesChangedTo

Add a signal match filter for properties changing to a specific
value.

Change-Id: I10f20ba03ae7c629d2c338c7975e0d32d9008e01
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
diff --git a/README.md b/README.md
index 5e9b597..d407603 100644
--- a/README.md
+++ b/README.md
@@ -56,6 +56,8 @@
 The available filters provided by PIM are:
 
 * none - A non-filter.
+* propertyChangedTo - Only match events when the specified property has
+the specified value.
 
 ----
 
diff --git a/examples/match2.yaml b/examples/match2.yaml
index 6fb9ff9..8cfa105 100644
--- a/examples/match2.yaml
+++ b/examples/match2.yaml
@@ -13,6 +13,10 @@
           interface: org.freedesktop.DBus.Properties
           member: PropertiesChanged
       filter:
-          name: none
+          name: propertyChangedTo
+          args:
+            - value: xyz.openbmc_project.Testing
+            - value: TestProperty
+            - value: teststring
 
 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/filters.hpp b/filters.hpp
index eb843a7..309fff6 100644
--- a/filters.hpp
+++ b/filters.hpp
@@ -106,6 +106,60 @@
     std::shared_ptr<holder::Base> _ptr;
 };
 
+namespace property_condition
+{
+
+/** @struct PropertyCondition
+ *  @brief Match filter functor that tests a property value.
+ *
+ *  @tparam T - The type of the property being tested.
+ *  @tparam U - The type of the condition checking functor.
+ */
+template <typename T, typename U>
+struct PropertyCondition
+{
+    PropertyCondition() = delete;
+    ~PropertyCondition() = default;
+    PropertyCondition(const PropertyCondition&) = default;
+    PropertyCondition & operator=(const PropertyCondition&) = delete;
+    PropertyCondition(PropertyCondition&&) = default;
+    PropertyCondition& operator=(PropertyCondition&&) = default;
+    PropertyCondition(const char *iface, const char *property, U &&condition) :
+        _iface(iface),
+        _property(property),
+        _condition(std::forward<U>(condition)) { }
+
+    /** @brief Test a property value.
+     *
+     * Extract the property from the PropertiesChanged
+     * message and run the condition test.
+     */
+    bool operator()(sdbusplus::message::message &msg) const
+    {
+        std::map<
+            std::string,
+            sdbusplus::message::variant<T>> properties;
+        const char *iface = nullptr;
+
+        msg.read(iface);
+        if(strcmp(iface, _iface))
+                return false;
+
+        msg.read(properties);
+        auto it = properties.find(_property);
+        if(it == properties.cend())
+            return false;
+
+        return _condition(it->second);
+    }
+
+    private:
+    const char *_iface;
+    const char *_property;
+    U _condition;
+};
+
+} // namespace property_condition
 } // namespace details
 
 /** @brief The default filter.  */
@@ -114,6 +168,19 @@
     return true;
 }
 
+/** @brief Implicit type deduction for constructing PropertyCondition.  */
+template <typename T>
+auto propertyChangedTo(
+        const char *iface,
+        const char *property,
+        T val)
+{
+    auto condition = [val = std::move(val)](auto arg){return arg == val;};
+    using U = decltype(condition);
+    return details::property_condition::PropertyCondition<T, U>(
+            iface, property, std::move(condition));
+}
+
 } // namespace filters
 } // namespace manager
 } // namespace inventory
diff --git a/test/Makefile.am b/test/Makefile.am
index 4026307..2c340ea 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -5,4 +5,5 @@
 phosphor_inventory_test_LDFLAGS = $(SYSTEMD_LIBS)
 phosphor_inventory_test_CFLAGS = $(SYSTEMD_CFLAGS)
 phosphor_inventory_test_LDADD = ${top_builddir}/manager.o \
+				${top_builddir}/filters.o \
 				${top_builddir}/server.o