#nullable enable using IPA.Config.Stores.Attributes; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace IPA.Config.Stores { /// /// A class providing an extension for to make it easy to use generated /// config stores. /// public static class GeneratedStore { /// /// The name of the assembly that internals must be visible to to allow internal protection. /// public const string AssemblyVisibilityTarget = GeneratedStoreImpl.GeneratedAssemblyName; /// /// Creates a generated of type , registers it to /// the object, and returns it. This also forces a synchronous config load via /// if is . /// /// /// /// must be a public non- class. /// It can also be internal, but in that case, then your assembly must have the following attribute /// to allow the generated code to reference it. /// /// [assembly: InternalsVisibleTo(IPA.Config.Stores.GeneratedStore.AssemblyVisibilityTarget)] /// /// /// /// Only fields and properties that are public or protected will be considered, and only properties /// where both the getter and setter are public or protected are considered. Any fields or properties /// with an applied to them are also ignored. Having properties be is not strictly /// necessary, however it allows the generated type to keep track of changes and lock around them so that the config will auto-save. /// /// /// All of the attributes in the namespace are handled as described by them. /// /// /// If the declares a public or protected, /// method Changed(), then that method may be called to artificially signal to the runtime that the content of the object /// has changed. That method will also be called after the write locks are released when a property is set anywhere in the owning /// tree. This will only be called on the outermost generated object of the config structure, even if the change being signaled /// is somewhere deep into the tree. /// /// /// Similarly, can declare a public or protected, /// method OnReload(), which will be called on the filesystem reader thread after the object has been repopulated with new data /// values. It will be called after the write lock for this object is released. This will only be called on the outermost generated /// object of the config structure. /// /// /// Similarly, can declare a public or protected, /// method CopyFrom(ConfigType) (the first parameter is the type it is defined on), which may be called to copy the properties from /// another object of its type easily, and more importantly, as only one change. Its body will be executed after the values have been copied. /// /// /// Similarly, can declare a public or protected, /// method ChangeTransaction() returning , which may be called to get an object representing a transactional /// change. This may be used to change a lot of properties at once without triggering a save multiple times. Ideally, this is used in a /// block or declaration. The returned from your implementation will have its /// called after Changed() is called, but before the write lock is released. /// Unless you have a very good reason to use the nested , avoid it. /// /// /// If is marked with , the resulting object will implement /// . Similarly, if implements , /// the resulting object will implement it and notify it too. /// /// /// the type to wrap /// the to register to /// whether to synchronously load the content, or trigger an async load /// a generated instance of as a special public static T Generated(this Config cfg, bool loadSync = true) where T : class { var ret = GeneratedStoreImpl.Create(); cfg.SetStore(ret as IConfigStore); if (loadSync) cfg.LoadSync(); else cfg.LoadAsync(); return ret; } /// /// Creates a generated store outside of the context of the config system. /// /// /// See for more information about how it behaves. /// /// the type to wrap /// a generated instance of implementing functionality described by /// public static T Create() where T : class => GeneratedStoreImpl.Create(); } }