// Anope IRC Services
//
// Copyright (C) 2003-2026 Anope Contributors
//
// Anope is free software. You can use, modify, and/or distribute it under the
// terms of version 2 of the GNU General Public License. See docs/LICENSE.txt
// for the complete terms of this license and docs/AUTHORS.txt for a list of
// contributors.
//
// Based on the original code of Epona by Lara
// Based on the original code of Services by Andy Church
//
// SPDX-License-Identifier: GPL-2.0-only
#pragma once
#include "anope.h"
#include "serialize.h"
#include "service.h"
#include "logger.h"
class Extensible;
class CoreExport ExtensibleBase
: public Service
{
protected:
std::map items;
ExtensibleBase(Module *m, const Anope::string &n);
~ExtensibleBase();
public:
virtual void Unset(Extensible *obj) = 0;
/* called when an object we are keep track of is serializing */
virtual void ExtensibleSerialize(const Extensible *, const Serializable *, Serialize::Data &) const { }
virtual void ExtensibleUnserialize(Extensible *, Serializable *, Serialize::Data &) { }
};
class CoreExport Extensible
{
public:
std::set extension_items;
virtual ~Extensible();
void UnsetExtensibles();
template T *GetExt(const Anope::string &name) const;
bool HasExt(const Anope::string &name) const;
template T *Extend(const Anope::string &name, const T &what);
template T *Extend(const Anope::string &name);
template T *Require(const Anope::string &name);
template void Shrink(const Anope::string &name);
static void ExtensibleSerialize(const Extensible *, const Serializable *, Serialize::Data &data);
static void ExtensibleUnserialize(Extensible *, Serializable *, Serialize::Data &data);
};
template
class BaseExtensibleItem
: public ExtensibleBase
{
protected:
virtual T *Create(Extensible *) = 0;
public:
BaseExtensibleItem(Module *m, const Anope::string &n) : ExtensibleBase(m, n) { }
~BaseExtensibleItem()
{
while (!items.empty())
{
std::map::iterator it = items.begin();
Extensible *obj = it->first;
T *value = static_cast(it->second);
obj->extension_items.erase(this);
items.erase(it);
delete value;
}
}
T *Set(Extensible *obj, const T &value)
{
T *t = Set(obj);
if (t)
*t = value;
return t;
}
T *Set(Extensible *obj)
{
T *t = Create(obj);
Unset(obj);
items[obj] = t;
obj->extension_items.insert(this);
return t;
}
void Unset(Extensible *obj) override
{
T *value = Get(obj);
items.erase(obj);
obj->extension_items.erase(this);
delete value;
}
T *Get(const Extensible *obj) const
{
std::map::const_iterator it = items.find(const_cast(obj));
if (it != items.end())
return static_cast(it->second);
return NULL;
}
bool HasExt(const Extensible *obj) const
{
return items.find(const_cast(obj)) != items.end();
}
T *Require(Extensible *obj)
{
T *t = Get(obj);
if (t)
return t;
return Set(obj);
}
};
template
class ExtensibleItem
: public BaseExtensibleItem
{
protected:
T *Create(Extensible *obj) override
{
return new T(obj);
}
public:
ExtensibleItem(Module *m, const Anope::string &n) : BaseExtensibleItem(m, n) { }
};
template
class PrimitiveExtensibleItem
: public BaseExtensibleItem
{
protected:
T *Create(Extensible *obj) override
{
return new T();
}
public:
PrimitiveExtensibleItem(Module *m, const Anope::string &n) : BaseExtensibleItem(m, n) { }
};
template<>
class PrimitiveExtensibleItem : public BaseExtensibleItem
{
protected:
bool *Create(Extensible *) override
{
return NULL;
}
public:
PrimitiveExtensibleItem(Module *m, const Anope::string &n) : BaseExtensibleItem(m, n) { }
};
template
class SerializableExtensibleItem
: public PrimitiveExtensibleItem
{
public:
SerializableExtensibleItem(Module *m, const Anope::string &n) : PrimitiveExtensibleItem(m, n) { }
void ExtensibleSerialize(const Extensible *e, const Serializable *s, Serialize::Data &data) const override
{
T *t = this->Get(e);
data.Store(this->name, *t);
}
void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) override
{
T t;
if (data.TryLoad(this->name, t))
this->Set(e, t);
else
this->Unset(e);
}
};
template<>
class SerializableExtensibleItem : public PrimitiveExtensibleItem
{
public:
SerializableExtensibleItem(Module *m, const Anope::string &n) : PrimitiveExtensibleItem(m, n) { }
void ExtensibleSerialize(const Extensible *e, const Serializable *s, Serialize::Data &data) const override
{
data.Store(this->name, this->HasExt(e));
}
void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) override
{
if (data.Load(this->name))
this->Set(e);
else
this->Unset(e);
}
};
template
struct ExtensibleRef final
: ServiceReference >
{
ExtensibleRef(const Anope::string &n) : ServiceReference >("Extensible", n) { }
};
template
T *Extensible::GetExt(const Anope::string &name) const
{
ExtensibleRef ref(name);
if (ref)
return ref->Get(this);
Log(LOG_DEBUG) << "GetExt for nonexistent type " << name << " on " << static_cast(this);
return NULL;
}
template
T *Extensible::Extend(const Anope::string &name, const T &what)
{
T *t = Extend(name);
if (t)
*t = what;
return t;
}
template
T *Extensible::Extend(const Anope::string &name)
{
ExtensibleRef ref(name);
if (ref)
return ref->Set(this);
Log(LOG_DEBUG) << "Extend for nonexistent type " << name << " on " << static_cast(this);
return NULL;
}
template
T *Extensible::Require(const Anope::string &name)
{
if (HasExt(name))
return GetExt(name);
else
return Extend(name);
}
template
void Extensible::Shrink(const Anope::string &name)
{
ExtensibleRef ref(name);
if (ref)
ref->Unset(this);
else
Log(LOG_DEBUG) << "Shrink for nonexistent type " << name << " on " << static_cast(this);
}