<commit_before>#include "Room.hpp"

#include <unordered_set>

#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QFile>
#include <QDebug>

#include "proto.hpp"
#include "Session.hpp"
#include "utils.hpp"
#include "parse.hpp"

namespace matrix {

RoomState::RoomState(const QJsonObject &info, lmdb::txn &txn, lmdb::dbi &member_db)
    : name_(info["name"].toString()),
      canonical_alias_(info["canonical_alias"].toString()),
      topic_(info["topic"].toString()),
      avatar_(info["avatar"].toString()) {
  const auto &as = info["aliases"].toArray();
  aliases_.reserve(as.size());
  std::transform(as.begin(), as.end(), std::back_inserter(aliases_),
                 [](const QJsonValue &v) {
                   return v.toString();
                 });

  lmdb::val id_val;
  lmdb::val state;
  auto cursor = lmdb::cursor::open(txn, member_db);
  while(cursor.get(id_val, state, MDB_NEXT)) {
    const auto id_str = QString::fromUtf8(id_val.data(), id_val.size());
    auto &member = members_by_id_.emplace(
      std::piecewise_construct,
      std::forward_as_tuple(id_str),
      std::forward_as_tuple(id_str, QJsonDocument::fromBinaryData(QByteArray(state.data(), state.size())).object())).first->second;
    record_displayname(member, nullptr);
  }
}

QJsonObject RoomState::to_json() const {
  QJsonObject o;
  if(!name_.isNull()) o["name"] = name_;
  if(!canonical_alias_.isNull()) o["canonical_alias"] = canonical_alias_;
  if(!topic_.isNull()) o["topic"] = topic_;
  if(!avatar_.isEmpty()) o["avatar"] = avatar_.toString(QUrl::FullyEncoded);

  QJsonArray aa;
  for(const auto &x : aliases_) {
    aa.push_back(x);
  }
  o["aliases"] = std::move(aa);

  return o;
}

QString RoomState::pretty_name(const QString &own_id) const {
  if(!name_.isEmpty()) return name_;
  if(!canonical_alias_.isEmpty()) return canonical_alias_;
  if(!aliases_.empty()) return aliases_[0];  // Non-standard, but matches vector-web
  // FIXME: Maintain earliest two IDs as state!
  auto ms = members();
  ms.erase(std::remove_if(ms.begin(), ms.end(), [&](const Member *m){ return m->id() == own_id; }), ms.end());
  if(ms.size() > 1) {
    std::partial_sort(ms.begin(), ms.begin() + 2, ms.end(),
                      [](const Member *a, const Member *b) {
                        return a->id() < b->id();
                      });
  }
  switch(ms.size()) {
  case 0: return QObject::tr("Empty room");
  case 1: return member_name(*ms[0]);
  case 2: return QObject::tr("%1 and %2").arg(member_name(*ms[0])).arg(member_name(*ms[1]));
  default: return QObject::tr("%1 and %2 others").arg(member_name(*ms[0])).arg(ms.size() - 1);
  }
}

QString RoomState::member_name(const Member &member) const {
  if(member.display_name().isEmpty()) {
    return member.id();
  }
  if(members_named(member.display_name()).size() == 1) {
    return member.display_name();
  }
  return QObject::tr("%1 (%2)").arg(member.display_name()).arg(member.id());
}

std::vector<Member *> &RoomState::members_named(QString displayname) {
  return members_by_displayname_.at(displayname.normalized(QString::NormalizationForm_C));
}
const std::vector<Member *> &RoomState::members_named(QString displayname) const {
  return members_by_displayname_.at(displayname.normalized(QString::NormalizationForm_C));
}

std::vector<const Member *> RoomState::members() const {
  std::vector<const Member *> result;
  result.reserve(members_by_id_.size());
  std::transform(members_by_id_.begin(), members_by_id_.end(), std::back_inserter(result),
                 [](auto &x) { return &x.second; });
  return result;
}

void RoomState::forget_displayname(const Member &member, QString old_name_in, Room *room) {
  if(!old_name_in.isEmpty()) {
    QString old_name = old_name_in.normalized(QString::NormalizationForm_C);
    auto &vec = members_named(old_name);
    QString other_name;
    const Member *other_member = nullptr;
    if(room && vec.size() == 2) {
      other_member = vec[0] == &member ? vec[1] : vec[0];
      other_name = member_name(*other_member);
    }
    vec.erase(std::remove(vec.begin(), vec.end(), &member), vec.end());
    if(vec.empty()) {
      members_by_displayname_.erase(old_name);
    }
    if(other_member) {
      room->member_name_changed(*other_member, other_name);
    }
  }
}

void RoomState::record_displayname(Member &member, Room *room) {
  if(member.display_name().isEmpty()) return;
  auto &vec = members_by_displayname_[member.display_name().normalized(QString::NormalizationForm_C)];
  Member *other_member = nullptr;
  QString other_old_name;
  if(vec.size() == 1) {
    // Third party existing name will become disambiguated
    other_member = vec[0];
    other_old_name = member_name(*vec[0]);
  }
  vec.push_back(&member);
  if(other_member && room) room->member_name_changed(*other_member, other_old_name);
}

const Member *RoomState::member(const QString &id) const {
  auto it = members_by_id_.find(id);
  if(it == members_by_id_.end()) return nullptr;
  return &it->second;
}
<commit_msg>Style<commit_after>#include "Room.hpp"

#include <unordered_set>

#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QFile>
#include <QDebug>

#include "proto.hpp"
#include "Session.hpp"
#include "utils.hpp"
#include "parse.hpp"

namespace matrix {

RoomState::RoomState(const QJsonObject &info, lmdb::txn &txn, lmdb::dbi &member_db)
    : name_(info["name"].toString()),
      canonical_alias_(info["canonical_alias"].toString()),
      topic_(info["topic"].toString()),
      avatar_(info["avatar"].toString()) {
  const auto &as = info["aliases"].toArray();
  aliases_.reserve(as.size());
  std::transform(as.begin(), as.end(), std::back_inserter(aliases_),
                 [](const QJsonValue &v) {
                   return v.toString();
                 });

  lmdb::val id_val;
  lmdb::val state;
  auto cursor = lmdb::cursor::open(txn, member_db);
  while(cursor.get(id_val, state, MDB_NEXT)) {
    const auto id_str = QString::fromUtf8(id_val.data(), id_val.size());
    auto &member = members_by_id_.emplace(
      std::piecewise_construct,
      std::forward_as_tuple(id_str),
      std::forward_as_tuple(id_str, QJsonDocument::fromBinaryData(QByteArray(state.data(), state.size())).object())).first->second;
    record_displayname(member, nullptr);
  }
}

QJsonObject RoomState::to_json() const {
  QJsonObject o;
  if(!name_.isNull()) o["name"] = name_;
  if(!canonical_alias_.isNull()) o["canonical_alias"] = canonical_alias_;
  if(!topic_.isNull()) o["topic"] = topic_;
  if(!avatar_.isEmpty()) o["avatar"] = avatar_.toString(QUrl::FullyEncoded);

  QJsonArray aa;
  for(const auto &x : aliases_) {
    aa.push_back(x);
  }
  o["aliases"] = std::move(aa);

  return o;
}

QString RoomState::pretty_name(const QString &own_id) const {
  if(!name_.isEmpty()) return name_;
  if(!canonical_alias_.isEmpty()) return canonical_alias_;
  if(!aliases_.empty()) return aliases_[0];  // Non-standard, but matches vector-web
  // FIXME: Maintain earliest two IDs as state!
  auto ms = members();
  ms.erase(std::remove_if(ms.begin(), ms.end(), [&](const Member *m){ return m->id() == own_id; }), ms.end());
  if(ms.size() > 1) {
    std::partial_sort(ms.begin(), ms.begin() + 2, ms.end(),
                      [](const Member *a, const Member *b) {
                        return a->id() < b->id();
                      });
  }
  switch(ms.size()) {
  case 0: return QObject::tr("Empty room");
  case 1: return member_name(*ms[0]);
  case 2: return QObject::tr("%1 and %2").arg(member_name(*ms[0])).arg(member_name(*ms[1]));
  default: return QObject::tr("%1 and %2 others").arg(member_name(*ms[0])).arg(ms.size() - 1);
  }
}

QString RoomState::member_name(const Member &member) const {
  if(member.display_name().isEmpty()) {
    return member.id();
  }
  if(members_named(member.display_name()).size() == 1) {
    return member.display_name();
  }
  return QObject::tr("%1 (%2)").arg(member.display_name()).arg(member.id());
}

std::vector<Member *> &RoomState::members_named(QString displayname) {
  return members_by_displayname_.at(displayname.normalized(QString::NormalizationForm_C));
}
const std::vector<Member *> &RoomState::members_named(QString displayname) const {
  return members_by_displayname_.at(displayname.normalized(QString::NormalizationForm_C));
}

std::vector<const Member *> RoomState::members() const {
  std::vector<const Member *> result;
  result.reserve(members_by_id_.size());
  std::transform(members_by_id_.begin(), members_by_id_.end(), std::back_inserter(result),
                 [](auto &x) { return &x.second; });
  return result;
}

void RoomState::forget_displayname(const Member &member, QString old_name_in, Room *room) {
  if(old_name_in.isEmpty()) return;

  QString old_name = old_name_in.normalized(QString::NormalizationForm_C);
  auto &vec = members_named(old_name);
  QString other_name;
  const Member *other_member = nullptr;
  if(room && vec.size() == 2) {
    other_member = vec[0] == &member ? vec[1] : vec[0];
    other_name = member_name(*other_member);
  }
  vec.erase(std::remove(vec.begin(), vec.end(), &member), vec.end());
  if(vec.empty()) {
    members_by_displayname_.erase(old_name);
  }
  if(other_member) {
    room->member_name_changed(*other_member, other_name);
  }
}

void RoomState::record_displayname(Member &member, Room *room) {
  if(member.display_name().isEmpty()) return;
  auto &vec = members_by_displayname_[member.display_name().normalized(QString::NormalizationForm_C)];
  Member *other_member = nullptr;
  QString other_old_name;
  if(vec.size() == 1) {
    // Third party existing name will become disambiguated
    other_member = vec[0];
    other_old_name = member_name(*vec[0]);
  }
  vec.push_back(&member);
  if(other_member && room) room->member_name_changed(*other_member, other_old_name);
}

const Member *RoomState::member(const QString &id) const {
  auto it = members_by_id_.find(id);
  if(it == members_by_id_.end()) return nullptr;
  return &it->second;
}

{'fraction_non_alphanumeric': 0.17172523961661343, 'fraction_numerical': 0.005191693290734824, 'mean_word_length': 4.5680933852140075, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7444826', 'n_tokens_mistral': 3456, 'n_tokens_neox': 3414, 'n_words': 619}