net/addr/sock: Add inet only type

Change-Id: I54737280e5eb7eb208afaca8ef4abb225d0a3b38
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include/stdplus/net/addr/sock.hpp b/include/stdplus/net/addr/sock.hpp
index 2830f83..dc97a9a 100644
--- a/include/stdplus/net/addr/sock.hpp
+++ b/include/stdplus/net/addr/sock.hpp
@@ -214,14 +214,14 @@
 namespace detail
 {
 
+using SockInAddrV = std::variant<Sock4Addr, Sock6Addr>;
 using SockAnyAddrV = std::variant<Sock4Addr, Sock6Addr, SockUAddr>;
 
-} // namespace detail
-
-struct SockAnyAddr : detail::SockAnyAddrV
+template <typename V>
+struct SockAnyAddr : V
 {
-    constexpr SockAnyAddr(auto&&... a) noexcept :
-        detail::SockAnyAddrV(std::forward<decltype(a)>(a)...)
+    constexpr SockAnyAddr(auto&&... args) :
+        V(std::forward<decltype(args)>(args)...)
     {}
 
     template <SockAddr T>
@@ -250,6 +250,59 @@
     }
 };
 
+} // namespace detail
+
+struct SockInAddr : detail::SockAnyAddr<detail::SockInAddrV>
+{
+    constexpr SockInAddr(In4Addr a, std::uint16_t p) noexcept :
+        detail::SockAnyAddr<detail::SockInAddrV>(std::in_place_type<Sock4Addr>,
+                                                 a, p)
+    {}
+    constexpr SockInAddr(Sock4Addr a) noexcept :
+        detail::SockAnyAddr<detail::SockInAddrV>(a)
+    {}
+    constexpr SockInAddr(In6Addr a, std::uint16_t p) noexcept :
+        detail::SockAnyAddr<detail::SockInAddrV>(std::in_place_type<Sock6Addr>,
+                                                 a, p)
+    {}
+    constexpr SockInAddr(Sock6Addr a) noexcept :
+        detail::SockAnyAddr<detail::SockInAddrV>(a)
+    {}
+};
+
+template <>
+struct IsSockAddr<SockInAddr> : std::true_type
+{};
+
+struct SockAnyAddr : detail::SockAnyAddr<detail::SockAnyAddrV>
+{
+    constexpr SockAnyAddr(In4Addr a, std::uint16_t p) noexcept :
+        detail::SockAnyAddr<detail::SockAnyAddrV>(std::in_place_type<Sock4Addr>,
+                                                  a, p)
+    {}
+    constexpr SockAnyAddr(Sock4Addr a) noexcept :
+        detail::SockAnyAddr<detail::SockAnyAddrV>(a)
+    {}
+    constexpr SockAnyAddr(In6Addr a, std::uint16_t p) noexcept :
+        detail::SockAnyAddr<detail::SockAnyAddrV>(std::in_place_type<Sock6Addr>,
+                                                  a, p)
+    {}
+    constexpr SockAnyAddr(Sock6Addr a) noexcept :
+        detail::SockAnyAddr<detail::SockAnyAddrV>(a)
+    {}
+    constexpr SockAnyAddr(std::string_view p) :
+        detail::SockAnyAddr<detail::SockAnyAddrV>(std::in_place_type<SockUAddr>,
+                                                  p)
+    {}
+    constexpr SockAnyAddr(SockUAddr a) noexcept :
+        detail::SockAnyAddr<detail::SockAnyAddrV>(a)
+    {}
+    constexpr SockAnyAddr(SockInAddr a) noexcept :
+        detail::SockAnyAddr<detail::SockAnyAddrV>(
+            std::visit([](auto v) { return detail::SockAnyAddrV(v); }, a))
+    {}
+};
+
 template <>
 struct IsSockAddr<SockAnyAddr> : std::true_type
 {};
@@ -367,6 +420,35 @@
 };
 
 template <>
+struct FromStr<SockInAddr>
+{
+    template <typename CharT>
+    constexpr SockInAddr operator()(std::basic_string_view<CharT> sv) const
+    {
+        if (sv.starts_with('['))
+        {
+            return FromStr<Sock6Addr>{}(sv);
+        }
+        return FromStr<Sock4Addr>{}(sv);
+    }
+};
+
+template <>
+struct ToStr<SockInAddr>
+{
+    using type = SockInAddr;
+    static inline constexpr std::size_t buf_size =
+        std::max({ToStr<Sock4Addr>::buf_size, ToStr<Sock6Addr>::buf_size});
+
+    template <typename CharT>
+    constexpr CharT* operator()(CharT* buf, const SockInAddr& v) const noexcept
+    {
+        return std::visit(
+            [buf]<typename T>(const T& t) { return ToStr<T>{}(buf, t); }, v);
+    }
+};
+
+template <>
 struct FromStr<SockAnyAddr>
 {
     template <typename CharT>
diff --git a/test/net/addr/sock.cpp b/test/net/addr/sock.cpp
index e326572..e5ed833 100644
--- a/test/net/addr/sock.cpp
+++ b/test/net/addr/sock.cpp
@@ -152,11 +152,40 @@
     EXPECT_EQ("a unix:a b", std::format("a {} b", SockUAddr("a"sv)));
 }
 
+TEST(SockInAddr, Basic)
+{
+    constexpr SockInAddr addr1(In4Addr{255}, 3959);
+    constexpr SockInAddr addr2(In6Addr{255}, 3959);
+    EXPECT_NE(addr1, addr2);
+    EXPECT_EQ(addr1, addr1);
+    auto buf = addr1.buf();
+    EXPECT_EQ(buf.len, sizeof(sockaddr_in));
+    EXPECT_EQ(addr1.sockaddrLen(), sizeof(sockaddr_in));
+    EXPECT_EQ(addr2.sockaddrLen(), sizeof(sockaddr_in6));
+}
+
+TEST(SockInAddr, FromStr)
+{
+    constexpr FromStr<SockInAddr> fs;
+    EXPECT_THROW(fs("abcd"sv), std::invalid_argument);
+    EXPECT_THROW(fs("/nope"sv), std::invalid_argument);
+    EXPECT_EQ((Sock4Addr{In4Addr{}, 30}), fs("0.0.0.0:30"sv));
+    EXPECT_EQ((Sock6Addr{In6Addr{}, 80, 0}), fs("[::]:80"sv));
+    EXPECT_THROW(fs("unix:/nope"sv), std::invalid_argument);
+}
+
+TEST(SockInAddr, ToStr)
+{
+    ToStrHandle<ToStr<SockInAddr>> tsh;
+    EXPECT_EQ("0.0.0.0:3949", tsh(Sock4Addr(In4Addr{}, 3949)));
+    EXPECT_EQ("a [::]:356 b",
+              std::format("a {} b", Sock6Addr(In6Addr{}, 356, 0)));
+}
+
 TEST(SockAnyAddr, Basic)
 {
-    constexpr SockAnyAddr addr1(std::in_place_type<Sock4Addr>, In4Addr{255},
-                                3959);
-    constexpr SockAnyAddr addr2(std::in_place_type<SockUAddr>, "/hi"sv);
+    constexpr SockAnyAddr addr1(In4Addr{255}, 3959);
+    constexpr SockAnyAddr addr2("/hi"sv);
     EXPECT_NE(addr1, addr2);
     EXPECT_EQ(addr1, addr1);
     auto buf = addr1.buf();