// Copyright (C) 2021-2022 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library.  This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3.  If not see
// <http://www.gnu.org/licenses/>.

// { dg-options "-std=gnu++23" }
// { dg-do run { target c++23 } }

#include <string_view>
#include <testsuite_hooks.h>

void
test01()
{
  struct R
  {
    const char* begin() const
    { return str; }

    const char* end() const
    { return str + std::char_traits<char>::length(str); }

    const char* str = "Home on the range";
  };

  R r;
  std::string_view s{r};
  VERIFY( s == r.str );
  VERIFY( s.data() == std::ranges::data(r) );
  VERIFY( s.size() == std::ranges::size(r) );

  struct R2 : R
  {
    using R::begin;
    using R::end;
    operator std::string_view() const { return "Out of range"; }
  };
  static_assert( std::ranges::contiguous_range<R2> );
  static_assert( std::ranges::sized_range<R2> );
  R2 r2;
  std::string_view s2(r2); // uses conversion to string_view
  VERIFY( s2 == "Out of range" );
  VERIFY( std::string_view(const_cast<const R2&>(r2)) == s2 );

  // And again using copy-initialization instead of direct-initialization.
  std::string_view s2_implicit = r2; // uses conversion to string_view
  VERIFY( s2_implicit == "Out of range" );
  VERIFY( std::string_view(const_cast<const R2&>(r2)) == s2_implicit );

  struct R3 : R
  {
    using R::begin;
    using R::end;
    operator const char*() { return "Orange"; }
  };
  static_assert( std::ranges::contiguous_range<R3> );
  static_assert( std::ranges::sized_range<R3> );
  R3 r3;
  std::string_view s3(r3); // uses conversion to const char*
  VERIFY( s3 == "Orange" );
  s3 = std::string_view(const_cast<const R3&>(r3)); // uses range constructor
  VERIFY( s3 == "Home on the range" );

  struct R4 : R
  {
    using R::begin;
    using R::end;
    operator std::string_view() { return "Strange"; }
  };
  static_assert( std::ranges::contiguous_range<R4> );
  static_assert( std::ranges::sized_range<R4> );
  R4 r4;
  std::string_view s4 = r4; // Uses conversion to string_view
  VERIFY( s4 == "Strange" );
  // Cannot construct from const R4 because of non-const conversion op:
  static_assert( ! std::is_constructible_v<std::string_view, const R4&> );

  struct R5 : R
  {
    using R::begin;
    using R::end;
    operator std::string_view() && { return "Stranger"; }
  };
  static_assert( std::ranges::contiguous_range<R5> );
  static_assert( std::ranges::sized_range<R5> );
  R5 r5;
  std::string_view s5(r5); // Uses range constructor
  VERIFY( s5 == r5.str );
  s5 = std::string_view(std::move(r5)); // In C++20 this used conversion op.
  VERIFY( s5 == r5.str );	        // In C++23 it uses range constructor.

  char arr[] = "arrangement\0with\0nulls";
  std::string_view sa = arr; // Does not use range constructor
  VERIFY( sa.data() == arr );
  VERIFY( sa == "arrangement" );
  VERIFY( std::end(sa) != std::end(arr) );
}

void
test02()
{
  using V1 = std::basic_string_view<signed char>;
  // range_value_t<V1> is not the right type
  static_assert( ! std::is_constructible_v<std::string_view, V1> );

  using V2 = std::basic_string_view<char, __gnu_cxx::char_traits<char>>;
  // V2::traits_type is not the right type
  static_assert( ! std::is_constructible_v<std::string_view, V2> );

  struct V3 : V2
  {
  private:
    using V2::traits_type;
  };
  // V3::traits_type is not a valid (accessible) type
  static_assert( std::is_constructible_v<std::string_view, V3> );

  struct V4 : V2
  {
    using traits_type = std::string_view::traits_type;
  };
  // V4::traits_type is the right type
  static_assert( std::is_constructible_v<std::string_view, V4> );
}

void
test03()
{
  struct R
  {
    char* begin() { return nullptr; }
    const char* begin() const noexcept { return nullptr; }

    char* end() { return nullptr; }
    const char* end() const noexcept { return nullptr; }
  };

  static_assert( ! noexcept(std::string_view(std::declval<R&>())) );
  static_assert( noexcept(std::string_view(std::declval<const R&>())) );
}

void
test04()
{
  struct R
  {
    const char* begin() const { return nullptr; }
    const char* end() const { return nullptr; }
  };

  R r;
  std::basic_string_view s(r); // Use deduction guide.

  static_assert( std::is_same_v<decltype(s), std::string_view> );
}

void
test05()
{
  struct R
  {
    const char* begin() const { return nullptr; }
    const char* end() const { return nullptr; }
  };

  // P2499R0 string_view range constructor should be explicit
  // P2516R0 string_view is implicitly convertible from what?
  static_assert( ! std::is_convertible_v<R, std::string_view> );
}

int main()
{
  test01();
  test02();
  test03();
  test04();
  test05();
}
