net/addr/subnet: Add FromStr conversion

Change-Id: I41ef5adda1e8784e10259989bea3316b94668b9b
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/test/net/addr/subnet.cpp b/test/net/addr/subnet.cpp
index 5024928..319a514 100644
--- a/test/net/addr/subnet.cpp
+++ b/test/net/addr/subnet.cpp
@@ -2,8 +2,12 @@
 
 #include <stdplus/net/addr/subnet.hpp>
 
+#include <string_view>
+
 #include <gtest/gtest.h>
 
+using std::literals::string_view_literals::operator""sv;
+
 namespace stdplus
 {
 
@@ -51,6 +55,17 @@
     EXPECT_TRUE(Subnet4(addr4Full, 0).contains(In4Addr{}));
 }
 
+TEST(Subnet4, FromStr)
+{
+    constexpr FromStr<Subnet4> fs;
+    EXPECT_THROW(fs("10"sv), std::invalid_argument);
+    EXPECT_THROW(fs("/10"sv), std::invalid_argument);
+    EXPECT_THROW(fs("0.0.0.0"sv), std::invalid_argument);
+    EXPECT_THROW(fs("0.0.0.0/"sv), std::invalid_argument);
+    EXPECT_THROW(fs("::/80"sv), std::invalid_argument);
+    EXPECT_EQ((SubnetAny{in_addr{}, 30}), fs("0.0.0.0/30"sv));
+}
+
 TEST(Subnet4, ToStr)
 {
     ToStrHandle<ToStr<Subnet4>> tsh;
@@ -111,6 +126,17 @@
     EXPECT_TRUE(Subnet6(addr6Full, 0).contains(In6Addr{}));
 }
 
+TEST(Subnet6, FromStr)
+{
+    constexpr FromStr<Subnet6> fs;
+    EXPECT_THROW(fs("10"sv), std::invalid_argument);
+    EXPECT_THROW(fs("/10"sv), std::invalid_argument);
+    EXPECT_THROW(fs("ff::"sv), std::invalid_argument);
+    EXPECT_THROW(fs("::/"sv), std::invalid_argument);
+    EXPECT_THROW(fs("0.0.0.0/0"sv), std::invalid_argument);
+    EXPECT_EQ((Subnet6{in6_addr{}, 80}), fs("::/80"sv));
+}
+
 TEST(Subnet6, ToStr)
 {
     ToStrHandle<ToStr<Subnet6>> tsh;
@@ -175,6 +201,18 @@
     EXPECT_FALSE(SubnetAny(addr4Full, 32).contains(InAnyAddr{In4Addr{}}));
 }
 
+TEST(SubnetAny, FromStr)
+{
+    constexpr FromStr<SubnetAny> fs;
+    EXPECT_THROW(fs("10"sv), std::invalid_argument);
+    EXPECT_THROW(fs("/10"sv), std::invalid_argument);
+    EXPECT_THROW(fs("0.0.0.0"sv), std::invalid_argument);
+    EXPECT_THROW(fs("0.0.0.0/"sv), std::invalid_argument);
+    EXPECT_EQ((SubnetAny{in_addr{}, 0}), fs("0.0.0.0/0"sv));
+    EXPECT_EQ((SubnetAny{in_addr{}, 30}), fs("0.0.0.0/30"sv));
+    EXPECT_EQ((SubnetAny{in6_addr{}, 80}), fs("::/80"sv));
+}
+
 TEST(SubnetAny, ToStr)
 {
     ToStrHandle<ToStr<SubnetAny>> tsh;