You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

106 lines
6.3 KiB

  1. #nullable enable
  2. using IPA.Config.Stores.Attributes;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.ComponentModel;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. namespace IPA.Config.Stores
  10. {
  11. /// <summary>
  12. /// A class providing an extension for <see cref="Config"/> to make it easy to use generated
  13. /// config stores.
  14. /// </summary>
  15. public static class GeneratedStore
  16. {
  17. /// <summary>
  18. /// The name of the assembly that internals must be visible to to allow internal protection.
  19. /// </summary>
  20. public const string AssemblyVisibilityTarget = GeneratedStoreImpl.GeneratedAssemblyName;
  21. /// <summary>
  22. /// Creates a generated <see cref="IConfigStore"/> of type <typeparamref name="T"/>, registers it to
  23. /// the <see cref="Config"/> object, and returns it. This also forces a synchronous config load via
  24. /// <see cref="Config.LoadSync"/> if <paramref name="loadSync"/> is <see langword="true"/>.
  25. /// </summary>
  26. /// <remarks>
  27. /// <para>
  28. /// <typeparamref name="T"/> must be a public non-<see langword="sealed"/> class.
  29. /// It can also be internal, but in that case, then your assembly must have the following attribute
  30. /// to allow the generated code to reference it.
  31. /// <code lang="csharp">
  32. /// [assembly: InternalsVisibleTo(IPA.Config.Stores.GeneratedStore.AssemblyVisibilityTarget)]
  33. /// </code>
  34. /// </para>
  35. /// <para>
  36. /// Only fields and properties that are public or protected will be considered, and only properties
  37. /// where both the getter and setter are public or protected are considered. Any fields or properties
  38. /// with an <see cref="IgnoreAttribute"/> applied to them are also ignored. Having properties be <see langword="virtual"/> is not strictly
  39. /// necessary, however it allows the generated type to keep track of changes and lock around them so that the config will auto-save.
  40. /// </para>
  41. /// <para>
  42. /// All of the attributes in the <see cref="Attributes"/> namespace are handled as described by them.
  43. /// </para>
  44. /// <para>
  45. /// If the <typeparamref name="T"/> declares a public or protected, <see langword="virtual"/>
  46. /// method <c>Changed()</c>, then that method may be called to artificially signal to the runtime that the content of the object
  47. /// has changed. That method will also be called after the write locks are released when a property is set anywhere in the owning
  48. /// tree. This will only be called on the outermost generated object of the config structure, even if the change being signaled
  49. /// is somewhere deep into the tree.
  50. /// </para>
  51. /// <para>
  52. /// Similarly, <typeparamref name="T"/> can declare a public or protected, <see langword="virtual"/>
  53. /// method <c>OnReload()</c>, which will be called on the filesystem reader thread after the object has been repopulated with new data
  54. /// values. It will be called <i>after</i> the write lock for this object is released. This will only be called on the outermost generated
  55. /// object of the config structure.
  56. /// </para>
  57. /// <para>
  58. /// Similarly, <typeparamref name="T"/> can declare a public or protected, <see langword="virtual"/>
  59. /// method <c>CopyFrom(ConfigType)</c> (the first parameter is the type it is defined on), which may be called to copy the properties from
  60. /// another object of its type easily, and more importantly, as only one change. Its body will be executed after the values have been copied.
  61. /// </para>
  62. /// <para>
  63. /// Similarly, <typeparamref name="T"/> can declare a public or protected, <see langword="virtual"/>
  64. /// method <c>ChangeTransaction()</c> returning <see cref="IDisposable"/>, which may be called to get an object representing a transactional
  65. /// 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
  66. /// <see langword="using"/> block or declaration. The <see cref="IDisposable"/> returned from your implementation will have its
  67. /// <see cref="IDisposable.Dispose"/> called <i>after</i> <c>Changed()</c> is called, but <i>before</i> the write lock is released.
  68. /// Unless you have a very good reason to use the nested <see cref="IDisposable"/>, avoid it.
  69. /// </para>
  70. /// <para>
  71. /// If <typeparamref name="T"/> is marked with <see cref="NotifyPropertyChangesAttribute"/>, the resulting object will implement
  72. /// <see cref="INotifyPropertyChanged"/>. Similarly, if <typeparamref name="T"/> implements <see cref="INotifyPropertyChanged"/>,
  73. /// the resulting object will implement it and notify it too.
  74. /// </para>
  75. /// </remarks>
  76. /// <typeparam name="T">the type to wrap</typeparam>
  77. /// <param name="cfg">the <see cref="Config"/> to register to</param>
  78. /// <param name="loadSync">whether to synchronously load the content, or trigger an async load</param>
  79. /// <returns>a generated instance of <typeparamref name="T"/> as a special <see cref="IConfigStore"/></returns>
  80. public static T Generated<T>(this Config cfg, bool loadSync = true) where T : class
  81. {
  82. var ret = GeneratedStoreImpl.Create<T>();
  83. cfg.SetStore(ret as IConfigStore);
  84. if (loadSync)
  85. cfg.LoadSync();
  86. else
  87. cfg.LoadAsync();
  88. return ret;
  89. }
  90. /// <summary>
  91. /// Creates a generated store outside of the context of the config system.
  92. /// </summary>
  93. /// <remarks>
  94. /// See <see cref="Generated{T}(Config, bool)"/> for more information about how it behaves.
  95. /// </remarks>
  96. /// <typeparam name="T">the type to wrap</typeparam>
  97. /// <returns>a generated instance of <typeparamref name="T"/> implementing functionality described by <see cref="Generated{T}(Config, bool)"/></returns>
  98. /// <seealso cref="Generated{T}(Config, bool)"/>
  99. public static T Create<T>() where T : class
  100. => GeneratedStoreImpl.Create<T>();
  101. }
  102. }