// Copyright 2015 The etcd Authors
// Copyright 2026 Leo Cheng
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
/// A membership snapshot as carried by a Raft snapshot (etcd's `ConfState`).
pub(all) struct ConfState {
  voters : Array[String]
  learners : Array[String]
  voters_outgoing : Array[String]
  learners_next : Array[String]
  auto_leave : Bool
} derive(Eq)

///|
/// The empty membership state, carried by a snapshot with no recorded
/// configuration (a fresh node, or one written before ConfState existed).
pub fn ConfState::empty() -> ConfState {
  {
    voters: [],
    learners: [],
    voters_outgoing: [],
    learners_next: [],
    auto_leave: false,
  }
}

///|
/// Whether this conf state records no membership at all.
pub fn ConfState::is_empty(self : ConfState) -> Bool {
  self.voters.is_empty() &&
  self.learners.is_empty() &&
  self.voters_outgoing.is_empty() &&
  self.learners_next.is_empty()
}

///|
fn set_eq(a : Array[String], b : Array[String]) -> Bool {
  let sa = a.copy()
  sa.sort()
  let sb = b.copy()
  sb.sort()
  sa == sb
}

///|
/// Whether two `ConfState`s describe the same configuration (etcd's
/// `ConfState.Equivalent`): the four id lists match as *sets* (order- and
/// nil/empty-insensitive) and the auto-leave flags agree.
pub fn ConfState::equivalent(self : ConfState, other : ConfState) -> Bool {
  set_eq(self.voters, other.voters) &&
  set_eq(self.learners, other.learners) &&
  set_eq(self.voters_outgoing, other.voters_outgoing) &&
  set_eq(self.learners_next, other.learners_next) &&
  self.auto_leave == other.auto_leave
}