net/addr/subnet: Add FromStr conversion
Change-Id: I41ef5adda1e8784e10259989bea3316b94668b9b
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include/stdplus/net/addr/subnet.hpp b/include/stdplus/net/addr/subnet.hpp
index 7f898c5..8a783ff 100644
--- a/include/stdplus/net/addr/subnet.hpp
+++ b/include/stdplus/net/addr/subnet.hpp
@@ -208,6 +208,22 @@
{
template <typename Subnet>
+struct SubnetFromStr
+{
+ template <typename CharT>
+ constexpr Subnet operator()(std::basic_string_view<CharT> sv) const
+ {
+ const auto pos = sv.rfind('/');
+ if (pos == sv.npos)
+ {
+ throw std::invalid_argument("Invalid subnet");
+ }
+ return {FromStr<typename Subnet::Addr>{}(sv.substr(0, pos)),
+ StrToInt<10, typename Subnet::Pfx>{}(sv.substr(pos + 1))};
+ }
+};
+
+template <typename Subnet>
struct SubnetToStr
{
using type = Subnet;
@@ -228,6 +244,15 @@
} // namespace detail
template <typename Addr, typename Pfx>
+struct FromStr<detail::Subnet46<Addr, Pfx>> :
+ detail::SubnetFromStr<detail::Subnet46<Addr, Pfx>>
+{};
+
+template <>
+struct FromStr<SubnetAny> : detail::SubnetFromStr<SubnetAny>
+{};
+
+template <typename Addr, typename Pfx>
struct ToStr<detail::Subnet46<Addr, Pfx>> :
detail::SubnetToStr<detail::Subnet46<Addr, Pfx>>
{};
diff --git a/src/net/addr/subnet.cpp b/src/net/addr/subnet.cpp
index d7cb24f..47f028f 100644
--- a/src/net/addr/subnet.cpp
+++ b/src/net/addr/subnet.cpp
@@ -15,6 +15,10 @@
template class Subnet46<In4Addr, uint8_t>;
template class Subnet46<In6Addr, uint8_t>;
+template Subnet4 SubnetFromStr<Subnet4>::operator()(std::string_view) const;
+template Subnet6 SubnetFromStr<Subnet6>::operator()(std::string_view) const;
+template SubnetAny SubnetFromStr<SubnetAny>::operator()(std::string_view) const;
+
template char* SubnetToStr<Subnet4>::operator()(char*, Subnet4) const noexcept;
template char* SubnetToStr<Subnet6>::operator()(char*, Subnet6) const noexcept;
template char* SubnetToStr<SubnetAny>::operator()(char*,
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;