OwlCore.Storage
OwlCore.Storage copied to clipboard
Standardizing basic file properties
We're still missing basic file properties like size and modified dates though, nobody has drafted an API in this area yet. You can extend the existing implementations and implement your own 'file property' interface, but we should really get this spec'd out.
We're going to discuss this on Discord and hash out what this might look like, then return with a rough draft.
This ticket filed for tracking.
Additional information
Pulled from the proposal
Storage properties
"Storage properties" are the additional information about a resource provided by most file systems. It's everything from the name to the "Last modified" date to the Thumbnail, and the majority of them can change / be changed.
In AbstractStorage, we simply copied the c# properties and recreated StorageFileContentProperties
, and even though we only added GetMusicPropertiesAsync
and made the property nullable, it caused a lot of confusion for everyone else who implemented it.
We'll continue using our strategy of "separate interfaces" for this. However, there are a LOT of storage properties we can add, so as long as we separate them into different interfaces, can safely leave "which" for later and instead figure out the "how".
There are 3 requirements
- The information must be accessible and always up-to-date.
- We must provide the option to notify the user of changes
- We must provide the option to change the value (separate interface, see mutability vs modifiability below)
This gives us 2 options:
- Events + properties +
SetThingAsync
methods, on an object retrieved from an async method. - Events +
GetThingAsync
Methods +SetThingAsync
methods
After some brainstorming, we have a basic skeleton that serves as a guide for any property set.
// The recommended pattern for file properties.
public interface IStorageProperties<T> : IDisposable
{
public T Value { get; }
public event EventHandler<T> ValueUpdated;
}
// Example usage with music properties.
public class MusicData
{
// todo
}
public interface IMusicProperties
{
// Must be behind an async method
public Task<IStorageProperties<MusicData>> GetMusicPropertiesAsync();
}
// If the implementor is capable, they can implement modifying props as well.
public interface IModifiableMusicProperties
{
public Task UpdateMusicProperties(MusicData newValue);
}
Doing it this way means
- There's a standard for getting and tracking groups of properties. Handy for property set designers and enables extensibility scenarios.
- You can easily check if a file/folder supports the interface beforehand, with try/catch or unboxing.
- You won't get name collisions between multiple property sets
- It's behind an async task, meaning it can retrieved asynchronously and easily disposed of, allowing for setup/teardown of sockets, file handles, etc..
- There's no standard for updating properties, it's up to whoever designs the interface. Update one thing at a time, or update the entire object- whatever is most appropriate for the situation (e.g.
UpdateMusicPropertiesAsync()
vsChangeNameAsync()
).