sharp-chat/SharpChatCommon/Channels/ChannelsContext.cs
flashwave 5a7756894b
First bits of the Context overhaul.
Reintroduces separate contexts for users, channels, connections (now split into sessions and connections) and user-channel associations.
It builds which is as much assurance as I can give about the stability of this commit, but its also the bare minimum of what i like to commit sooooo
A lot of things still need to be broadcast through events throughout the application in order to keep states consistent but we'll cross that bridge when we get to it.
I really need to stop using that phrase thingy, I'm overusing it.
2025-05-03 02:49:51 +00:00

193 lines
6.1 KiB
C#

using SharpChat.Snowflake;
namespace SharpChat.Channels;
public class ChannelsContext(RandomSnowflake snowflake) {
private readonly Dictionary<long, Channel> Channels = [];
private Channel? DefaultChannel = null;
private readonly Lock @lock = new();
public bool ChannelExists(Func<Channel, bool> predicate) {
lock(@lock)
return Channels.Values.Any(predicate);
}
public bool ChannelExists(string name) {
return ChannelExists(c => c.NameEquals(name));
}
public Channel GetDefaultChannel() {
lock(@lock) {
DefaultChannel ??= GetChannel(c => c.IsPublic && !c.IsTemporary) ?? throw new NoDefaultChannelException();
return DefaultChannel;
}
}
public void SetDefaultChannel(Channel channel)
=> SetDefaultChannel(channel.Name);
public void SetDefaultChannel(string channelName) {
lock(@lock)
DefaultChannel = GetChannel(channelName) ?? throw new ChannelNotFoundException(nameof(channelName));
}
public Channel? GetChannel(Func<Channel, bool> predicate) {
lock(@lock)
return Channels.Values.FirstOrDefault(predicate);
}
public Channel? GetChannel(string name) {
return GetChannel(c => c.NameEquals(name));
}
public Channel? GetChannel(long id) {
lock(@lock)
return Channels.TryGetValue(id, out Channel? channel) ? channel : null;
}
public IEnumerable<Channel> GetChannels() {
lock(@lock)
return [.. Channels.Values];
}
public IEnumerable<Channel> GetChannels(IEnumerable<long> ids) {
return [.. ids.Select(GetChannel).Where(c => c is not null).Cast<Channel>()];
}
public IEnumerable<Channel> GetChannels(Func<Channel, bool> predicate) {
lock(@lock)
return [.. Channels.Values.Where(predicate)];
}
public IEnumerable<Channel> GetChannels(IEnumerable<string> names) {
return GetChannels(c => names.Any(n => c.NameEquals(n)));
}
public IEnumerable<Channel> GetChannels(int maxRank) {
return GetChannels(c => c.Rank <= maxRank);
}
public Channel CreateChannel(
string name,
string password = "",
bool temporary = false,
int rank = 0,
string ownerId = "",
long? id = null
) {
if(!Channel.CheckName(name))
throw new ChannelNameFormatException(nameof(name));
lock(@lock) {
if(ChannelExists(name))
throw new ChannelExistsException(nameof(name));
id ??= snowflake.Next();
if(Channels.ContainsKey(id.Value))
throw new ChannelExistsException(nameof(id));
Channel channel = new(id.Value, name, password, temporary, rank, ownerId);
Channels.Add(id.Value, channel);
return channel;
}
}
public ChannelDiff UpdateChannel(
long id,
string? name = null,
string? password = null,
bool? temporary = null,
int? rank = null,
string? ownerId = null
) => UpdateChannelInternal(
GetChannel(id) ?? throw new ChannelNotFoundException(nameof(id)),
name, password, temporary, rank, ownerId
);
public ChannelDiff UpdateChannel(
string currentName,
string? name = null,
string? password = null,
bool? temporary = null,
int? rank = null,
string? ownerId = null
) => UpdateChannelInternal(
GetChannel(currentName) ?? throw new ChannelNotFoundException(nameof(currentName)),
name, password, temporary, rank, ownerId
);
public ChannelDiff UpdateChannel(
Channel channel,
string? name = null,
string? password = null,
bool? temporary = null,
int? rank = null,
string? ownerId = null
) => UpdateChannel(channel.Id, name, password, temporary, rank, ownerId);
private ChannelDiff UpdateChannelInternal(
Channel channel,
string? name = null,
string? password = null,
bool? temporary = null,
int? rank = null,
string? ownerId = null
) {
lock(@lock) {
StringDiff nameDiff = new(channel.Name, name);
if(nameDiff.Changed) {
if(!Channel.CheckName(nameDiff.After))
throw new ChannelNameFormatException(nameof(name));
if(ChannelExists(nameDiff.After))
throw new ChannelExistsException(nameof(name));
channel.Name = nameDiff.After;
}
StringDiff passwordDiff = new(channel.Password, password);
if(passwordDiff.Changed)
channel.Password = passwordDiff.After;
ValueDiff<bool> temporaryDiff = new(channel.IsTemporary, temporary);
if(temporaryDiff.Changed)
channel.IsTemporary = temporaryDiff.After;
ValueDiff<int> rankDiff = new(channel.Rank, rank);
if(rankDiff.Changed)
channel.Rank = rankDiff.After;
StringDiff ownerIdDiff = new(channel.OwnerId, ownerId);
if(ownerIdDiff.Changed)
channel.OwnerId = ownerIdDiff.After;
return new(
channel,
nameDiff,
passwordDiff,
temporaryDiff,
rankDiff,
ownerIdDiff
);
}
}
public void RemoveChannel(long id)
=> RemoveChannelInternal(GetChannel(id) ?? throw new ChannelNotFoundException(nameof(id)), nameof(id));
public void RemoveChannel(Channel channel)
=> RemoveChannel(channel.Name);
public void RemoveChannel(string name)
=> RemoveChannelInternal(GetChannel(name) ?? throw new ChannelNotFoundException(nameof(name)), nameof(name));
private void RemoveChannelInternal(Channel channel, string argName) {
lock(@lock) {
Channel defaultChannel = GetDefaultChannel();
if(channel == defaultChannel || defaultChannel.NameEquals(channel.Name))
throw new ChannelIsDefaultException(argName);
Channels.Remove(channel.Id);
}
}
}