using System.Collections.Immutable; using System.Reactive.Linq; namespace InnovEnergy.Lib.Protocols.DBus.Daemon; using NameOwnerChanged = ValueTuple; using ServiceDict = ImmutableDictionary; public enum ServiceChange { NoChange, IdAdded, IdRemoved, BusNameAdded, BusNameRemoved, } public sealed record ServiceChanged(ServiceChange Change, String BusNameOrId, DBusService Service, ImmutableDictionary AllServices); public static class Resolver { private static readonly ServiceChanged InitialState = NoChange(ImmutableDictionary.Empty); public static IObservable ObserveServices(this DBusDaemonConnection connection) { var names = connection .ListNames() .Result; var newIds = names .Where(DBusDaemonApi.IsBusId) .Select(NewBusId); var newBusNames = names .Where(DBusDaemonApi.IsBusName) .Select(NewBusName) .Where(t => t != null) .Cast(); var ownerChanged = connection .ObserveSignalMessages(DBusDaemonApi.ServiceName, DBusDaemonApi.Interface, DBusDaemonApi.ObjectPath, "NameOwnerChanged") .Select(m => m.Payload!) .OfType(); var init = newIds.Concat(newBusNames).ToList(); return ownerChanged .StartWith(init) .Scan(InitialState, OnNameOwnerChanged) .Where(c => c.Change != ServiceChange.NoChange); NameOwnerChanged? NewBusName(String busName) { var ownerId = connection .GetNameOwner(busName) .ContinueWith(t => t.IsCompletedSuccessfully ? t.Result : null) .Result; if (ownerId is null) return null; return (busName, "", ownerId); } } private static ServiceChanged OnNameOwnerChanged(ServiceChanged serviceChange, NameOwnerChanged ownerChange) { var allServices = serviceChange.AllServices; if (ownerChange.IsInvalid()) return NoChange(allServices); var (nameOrId, oldOwner, newOwner) = ownerChange; if (ownerChange.IdWasRemoved()) { var id = nameOrId; allServices.TryGetValue(id, out var service); service ??= new DBusService(id); allServices = allServices.Remove(id); //Console.WriteLine("Removed " + id); return new ServiceChanged(ServiceChange.IdRemoved, id, service, allServices); } if (ownerChange.IdWasAdded()) { var id = nameOrId; var service = new DBusService(id); allServices = allServices.SetItem(id, service); //Console.WriteLine("Added " + id); return new ServiceChanged(ServiceChange.IdAdded, id, service, allServices); } if (ownerChange.BusNameWasRemoved()) { var busName = nameOrId; var id = oldOwner; allServices.TryGetValue(busName, out var service); if (service == null) { Console.WriteLine("BusNameRemoved: should not happen"); service = new DBusService(busName); } allServices = allServices.Remove(busName); if (allServices.TryGetValue(id, out var owner)) owner.BusNames.Remove(busName); //Console.WriteLine("Removed " + busName); return new ServiceChanged(ServiceChange.BusNameRemoved, busName, service, allServices); } if (ownerChange.BusNameWasAdded()) { var busName = nameOrId; var id = newOwner; if (!allServices.TryGetValue(id, out var service)) { Console.WriteLine("BusNameAdded: should not happen"); service = new DBusService(id); allServices = allServices.Add(id,service); } service.BusNames.Add(busName); allServices = allServices.SetItem(busName, service); //Console.WriteLine("Added " + busName); return new ServiceChanged(ServiceChange.BusNameAdded, busName, service, allServices); } Console.WriteLine("NoChange: should not happen"); return NoChange(allServices); } private static ServiceChanged NoChange(ServiceDict allServices) { return new ServiceChanged(ServiceChange.NoChange, null!, null!, allServices); } private static NameOwnerChanged NewBusId(String id) => (id, "", id); private static Boolean IsInvalid(this NameOwnerChanged change) { var (name, oldOwner, newOwner) = change; return name == "" || oldOwner == newOwner; } private static Boolean IdWasAdded(this NameOwnerChanged change) { var (id, oldOwner, newOwner) = change; return id.IsBusId() && oldOwner == "" && newOwner == id; } private static Boolean IdWasRemoved(this NameOwnerChanged change) { var (id, oldOwner, newOwner) = change; return id.IsBusId() && oldOwner == id && newOwner == ""; } private static Boolean BusNameWasAdded(this NameOwnerChanged change) { var (name, _, newOwner) = change; return name.IsBusName() && newOwner.IsBusId(); } private static Boolean BusNameWasRemoved(this NameOwnerChanged change) { var (name, oldOwner, _) = change; return name.IsBusName() && oldOwner.IsBusId(); } }