// Copyright (C) 2020-2025 Free Software Foundation, Inc.

// This file is part of GCC.

// GCC 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.

// GCC 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 GCC; see the file COPYING3.  If not see
// <http://www.gnu.org/licenses/>.

#include "rust-ast-fragment.h"

namespace Rust {
namespace AST {

Fragment::Fragment (FragmentKind kind, std::vector<SingleASTNode> nodes,
		    std::vector<std::unique_ptr<AST::Token>> tokens)
  : kind (kind), nodes (std::move (nodes)), tokens (std::move (tokens))
{}

Fragment::Fragment (Fragment const &other) : kind (other.get_kind ())
{
  *this = other;
}

Fragment &
Fragment::operator= (Fragment const &other)
{
  kind = other.get_kind ();

  nodes.clear ();
  nodes.reserve (other.nodes.size ());
  for (auto &n : other.nodes)
    nodes.push_back (n);

  tokens.clear ();
  tokens.reserve (other.tokens.size ());
  for (auto &t : other.tokens)
    tokens.emplace_back (t->clone_token ());

  return *this;
}

Fragment
Fragment::create_error ()
{
  return Fragment (FragmentKind::Error, {}, {});
}

Fragment
Fragment::create_empty ()
{
  return Fragment (FragmentKind::Complete, {}, {});
}

Fragment::Fragment (std::vector<AST::SingleASTNode> nodes,
		    std::vector<std::unique_ptr<AST::Token>> tokens)
  : kind (FragmentKind::Complete), nodes (std::move (nodes)),
    tokens (std::move (tokens))
{}

Fragment::Fragment (std::vector<AST::SingleASTNode> nodes,
		    std::unique_ptr<AST::Token> token)
  : kind (FragmentKind::Complete), nodes (std::move (nodes))
{
  tokens.emplace_back (std::move (token));
}

std::vector<SingleASTNode> &
Fragment::get_nodes ()
{
  return nodes;
}

std::vector<std::unique_ptr<AST::Token>> &
Fragment::get_tokens ()
{
  return tokens;
}

FragmentKind
Fragment::get_kind () const
{
  return kind;
}

bool
Fragment::is_error () const
{
  return get_kind () == FragmentKind::Error;
}

bool
Fragment::should_expand () const
{
  return !is_error ();
}

bool
Fragment::is_expression_fragment () const
{
  return is_single_fragment_of_kind (SingleASTNode::NodeType::EXPRESSION);
}

bool
Fragment::is_type_fragment () const
{
  return is_single_fragment_of_kind (SingleASTNode::NodeType::TYPE);
}

std::unique_ptr<Expr>
Fragment::take_expression_fragment ()
{
  assert_single_fragment (SingleASTNode::NodeType::EXPRESSION);
  return nodes[0].take_expr ();
}

std::unique_ptr<Type>
Fragment::take_type_fragment ()
{
  assert_single_fragment (SingleASTNode::NodeType::TYPE);
  return nodes[0].take_type ();
}

void
Fragment::accept_vis (ASTVisitor &vis)
{
  for (auto &node : nodes)
    node.accept_vis (vis);
}

bool
Fragment::is_single_fragment () const
{
  return nodes.size () == 1;
}

bool
Fragment::is_single_fragment_of_kind (SingleASTNode::NodeType expected) const
{
  return is_single_fragment () && nodes[0].get_kind () == expected;
}

void
Fragment::assert_single_fragment (SingleASTNode::NodeType expected) const
{
  static const std::map<SingleASTNode::NodeType, const char *> str_map = {
    {SingleASTNode::NodeType::ASSOC_ITEM, "associated item"},
    {SingleASTNode::NodeType::ITEM, "item"},
    {SingleASTNode::NodeType::TYPE, "type"},
    {SingleASTNode::NodeType::EXPRESSION, "expr"},
    {SingleASTNode::NodeType::STMT, "stmt"},
    {SingleASTNode::NodeType::EXTERN, "extern"},
  };

  auto actual = nodes[0].get_kind ();
  auto fail = false;

  if (!is_single_fragment ())
    {
      rust_error_at (UNDEF_LOCATION, "fragment is not single");
      fail = true;
    }

  if (actual != expected)
    {
      rust_error_at (
	UNDEF_LOCATION,
	"invalid fragment operation: expected %qs node, got %qs node",
	str_map.find (expected)->second,
	str_map.find (nodes[0].get_kind ())->second);
      fail = true;
    }

  rust_assert (!fail);
}

} // namespace AST
} // namespace Rust