public Task MoveFileAsync(IFileInfo file, AlphaNumericString tag, AlphaNumericString destinationTag, CancellationToken cancellationToken = default) { var destinationTagPath = GetTagPath(destinationTag); if (!Directory.Exists(destinationTagPath)) { Directory.CreateDirectory(destinationTagPath); } var filePath = GetFilePath(tag, file); var destinationPath = GetFilePath(destinationTag, file); File.Move(filePath, destinationPath); if (tag != string.Empty) { var tagPath = GetTagPath(tag); if (!Directory.GetDirectories(tagPath).Any() && !Directory.GetFiles(tagPath).Any()) { Directory.Delete(tagPath); } } return(Task.CompletedTask); }
public async Task <bool> CreateFileAsync(IFile file, AlphaNumericString tag, CancellationToken cancellationToken = default) { if (file == null) { throw new ArgumentNullException(nameof(file)); } var tagPath = GetTagPath(tag); if (!Directory.Exists(tagPath)) { Directory.CreateDirectory(tagPath); } var filePath = GetFilePath(tag, file); using (var fs = File.Open(filePath, FileMode.Create)) { await file.CopyToAsync(fs, cancellationToken); await fs.FlushAsync(cancellationToken); } return(true); }
/// <summary> /// Creates a prefixed storage of the given <paramref name="storage"/> where /// all tags will be prefixed with the given <paramref name="prefix"/>. /// </summary> /// <param name="storage">The storage to prefix.</param> /// <param name="prefix">The non-null or empty tag that should prefix all the tags.</param> /// <exception cref="ArgumentNullException">If <paramref name="storage"/> is null.</exception> /// <exception cref="ArgumentException">if <paramref name="prefix"/> is null or white space.</exception> public PrefixedStorage(IStorage storage, AlphaNumericString prefix) { if (string.IsNullOrWhiteSpace(prefix)) { throw new ArgumentException("Prefix must not be null or white space."); } Prefix = prefix; _storage = storage ?? throw new ArgumentNullException(nameof(storage)); }
/// <summary> /// Deletes the given <paramref name="tag"/> and all the files associated with the <paramref name="tag"/>. /// </summary> /// <param name="tag">The tag to remove.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> /// <returns> /// True if the tag and all its underlying files were successfully deleted, otherwise false if there was /// no tag corresponding to the given <paramref name="tag"/>. /// </returns> public Task <bool> DeleteTagAsync(AlphaNumericString tag, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); if (_files.TryGetValue(tag, out var files)) { files.Clear(); } return(Task.FromResult(_files.Remove(tag))); }
public Task <IFile> GetFileAsync(IFileInfo fileInfo, AlphaNumericString tag, CancellationToken cancellationToken = default) { var filePath = GetFilePath(tag, fileInfo); if (!File.Exists(filePath)) { return(Task.FromResult <IFile>(null)); } var file = new FileSystemFile(filePath); return(Task.FromResult <IFile>(file)); }
public IAsyncEnumerable <IFile> GetFiles(AlphaNumericString tag) { var tagPath = GetTagPath(tag); if (!Directory.Exists(tagPath)) { return(AsyncEnumerable.Empty <IFile>()); } return(Directory.GetFiles(tagPath) .Select(path => new FileSystemFile(path)) .Cast <IFile>() .ToAsyncEnumerable()); }
/// <summary> /// Returns the file associated with the given <paramref name="fileInfo"/> and <paramref name="tag"/>. /// If there was no file associated with the <paramref name="fileInfo"/> and <paramref name="tag"/>, /// null will be returned, otherwise the file. /// </summary> /// <param name="fileInfo">The information about the file to retrieve.</param> /// <param name="tag">The tag the file is associated with.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> /// <returns> /// A <see cref="Task{TResult}"/> containing the file associated with the <paramref name="fileInfo"/> and <paramref name="tag"/>. /// If no such file exists, the <see cref="Task{TResult}"/> will contain null. /// </returns> /// <exception cref="ArgumentNullException">If <paramref name="fileInfo"/> is null.</exception> public Task <IFile> GetFileAsync(IFileInfo fileInfo, AlphaNumericString tag, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); if (fileInfo == null) { throw new ArgumentNullException(nameof(fileInfo)); } var file = _files.TryGetValue(tag, out var files) ? files.FirstOrDefault(f => f.FileName == fileInfo.FileName) : null; return(Task.FromResult(file)); }
/// <summary> /// Saves the file in memory and associates the file with the given <paramref name="tag"/>. /// If a file with the same name exists, the file will be replaced with the given <paramref name="file"/>. /// </summary> /// <param name="file">The file to save to the memory storage.</param> /// <param name="tag">The tag to associate the file with.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> /// <exception cref="ArgumentNullException">If <paramref name="file"/> is null.</exception> public async Task <bool> CreateFileAsync(IFile file, AlphaNumericString tag, CancellationToken cancellationToken = default) { if (file == null) { throw new ArgumentNullException(nameof(file)); } if (!_files.TryGetValue(tag, out var files)) { files = new HashSet <IFile>(FileHelpers.FileComparer); _files.Add(tag, files); } if (files.Contains(file)) { files.Remove(file); } files.Add(await file.ToMemoryAsync()); return(true); }
public Task <bool> DeleteFileAsync(IFileInfo file, AlphaNumericString tag, CancellationToken cancellationToken = default) { var filePath = GetFilePath(tag, file); if (!File.Exists(filePath)) { return(Task.FromResult(false)); } File.Delete(filePath); var tagPath = GetTagPath(tag); if (tag != AlphaNumericString.Empty && !Directory.GetFiles(tagPath).Any() && !Directory.GetDirectories(tagPath).Any()) { Directory.Delete(tagPath); } return(Task.FromResult(true)); }
public Task <bool> DeleteTagAsync(AlphaNumericString tag, CancellationToken cancellationToken = default) { var path = GetTagPath(tag); if (!Directory.Exists(path)) { return(Task.FromResult(false)); } var filesInTag = Directory.GetFiles(path); foreach (var file in filesInTag) { File.Delete(file); } if (tag != AlphaNumericString.Empty) { Directory.Delete(path); } return(Task.FromResult(true)); }
/// <summary> /// Re-associates the file corresponding to the given <paramref name="file"/> and <paramref name="tag"/> /// to the <paramref name="destinationTag"/>. /// If the <paramref name="tag"/> is equal to <paramref name="destinationTag"/>, no operation will be performed. /// </summary> /// <param name="file">The file to associate with the <paramref name="destinationTag"/>.</param> /// <param name="tag">The tag the file is currently associated with.</param> /// <param name="destinationTag">The tag the file should be associated with.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> /// <returns> /// A <see cref="Task"/> representing the asynchronous operation. /// </returns> /// <exception cref="ArgumentNullException">If <paramref name="file"/> is null.</exception> public async Task MoveFileAsync(IFileInfo file, AlphaNumericString tag, AlphaNumericString destinationTag, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); if (file == null) { throw new ArgumentNullException(nameof(file)); } if (tag == destinationTag) { return; } var existingFile = await GetFileAsync(file, tag, cancellationToken); if (existingFile == null) { return; } await CreateFileAsync(existingFile, destinationTag, cancellationToken); await DeleteFileAsync(existingFile, tag, cancellationToken); }
/// <summary> /// Deletes the file associated with the given <paramref name="file"/> and <paramref name="tag"/>. /// If the file doesn't exist, false will be returned, otherwise true. /// </summary> /// <param name="file">The file to remove.</param> /// <param name="tag">The tag the file is located in.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> /// <returns> /// True if the file was successfully deleted, otherwise false if there was no file associated with the given <paramref name="file"/> /// and <paramref name="tag"/>. /// </returns> /// <exception cref="ArgumentNullException">If <paramref name="file"/> is null.</exception> public Task <bool> DeleteFileAsync(IFileInfo file, AlphaNumericString tag, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); if (file == null) { throw new ArgumentNullException(nameof(file)); } if (!_files.TryGetValue(tag, out var files)) { return(Task.FromResult(false)); } var existingFile = files.FirstOrDefault(f => f.FileName == file.FileName); if (existingFile == null) { return(Task.FromResult(false)); } files.Remove(existingFile); return(Task.FromResult(true)); }
/// <summary> /// Returns a collection of zero or more files associated with the given <paramref name="tag"/>. /// </summary> /// <param name="tag">The tag to retrieve all the associated files from.</param> /// <returns> /// An <see cref="IAsyncEnumerable{T}"/> containing the zero or more files associated with the /// given <paramref name="tag"/>. /// </returns> public IAsyncEnumerable <IFile> GetFiles(AlphaNumericString tag) { return(_files.TryGetValue(tag, out var files) ? files.ToAsyncEnumerable() : AsyncEnumerable <IFile> .Empty); }
/// <summary> /// Re-associates the given <paramref name="file"/>, currently associated with the given <paramref name="tag"/>, /// with the <paramref name="destinationTag"/> at the underlying storage, iff the storage has the permission /// <see cref="Permission.Move"/>. /// </summary> /// <param name="file">The file to re-associate with a new tag.</param> /// <param name="tag">The tag the file is currently associated with.</param> /// <param name="destinationTag">The new tag the file should be associated with.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> /// <returns> /// A <see cref="Task"/> representing the asynchronous operation. /// </returns> public Task MoveFileAsync(IFileInfo file, AlphaNumericString tag, AlphaNumericString destinationTag, CancellationToken cancellationToken = default) { ThrowIfNoPermission(Permission.Move); return(_storage.MoveFileAsync(file, tag, destinationTag, cancellationToken)); }
/// <summary> /// Will always return an empty collection of files. /// </summary> /// <param name="tag">The tag that doesn't make any difference at all.</param> /// <returns> /// An <see cref="IAsyncEnumerable{T}"/> of <see cref="IFile"/> that will /// always return zero elements. /// </returns> public IAsyncEnumerable <IFile> GetFiles(AlphaNumericString tag) { return(AsyncEnumerable <IFile> .Empty); }
/// <summary> /// Will always return null. /// </summary> /// <param name="fileInfo">The file that will not be retrieved.</param> /// <param name="tag">The tag that doesn't make any difference at all.</param> /// <param name="cancellationToken">The cancellation token that has nothing to say.</param> /// <returns> /// A <see cref="Task{TResult}"/> representing the asynchronous operation, that will always contain null. /// </returns> public Task <IFile> GetFileAsync(IFileInfo fileInfo, AlphaNumericString tag, CancellationToken cancellationToken = default) { return(Task.FromResult <IFile>(null)); }
/// <summary> /// Does not delete any tag at all. /// </summary> /// <param name="tag">The tag that will not be deleted.</param> /// <param name="cancellationToken">The cancellation token that has nothing to say.</param> /// <returns> /// A <see cref="Task"/> representing the asynchronous operation, that will always /// return false. /// </returns> public Task <bool> DeleteTagAsync(AlphaNumericString tag, CancellationToken cancellationToken = default) { return(Task.FromResult(false)); }
/// <summary> /// Does not delete any file at all. /// </summary> /// <param name="file">The file to not delete.</param> /// <param name="tag">The tag that the doesn't make any difference at all.</param> /// <param name="cancellationToken">The cancellation token that has nothing to say.</param> /// <returns> /// A <see cref="Task"/> representing the asynchronous operation, that will /// always contain false. /// </returns> public Task <bool> DeleteFileAsync(IFileInfo file, AlphaNumericString tag, CancellationToken cancellationToken = default) { return(Task.FromResult(false)); }
/// <summary> /// Will not move any files at all. /// </summary> /// <param name="file">The file that will not be moved.</param> /// <param name="tag">The tag that doesn't make any difference at all.</param> /// <param name="destinationTag">The destination tag that doesn't make any difference at all.</param> /// <param name="cancellationToken">The cancellation token that has nothing to say.</param> /// <returns> /// A <see cref="Task"/> representing the asynchronous operation. /// </returns> public Task MoveFileAsync(IFileInfo file, AlphaNumericString tag, AlphaNumericString destinationTag, CancellationToken cancellationToken = default) { return(Task.FromResult(0)); }
/// <summary> /// Deletes the given <paramref name="file"/>, associated with the given tag, from the underlying storage. /// The tag will prefixed with <see cref="Prefix"/> before being queried by the underlying storage. /// </summary> /// <param name="file">The file to delete from the underlying storage.</param> /// <param name="tag">The tag the file is associated with. The tag will be prefixed.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> /// <returns> /// A flag indicating whether the file was successfully deleted from the underlying storage or not. /// </returns> public Task <bool> DeleteFileAsync(IFileInfo file, AlphaNumericString tag, CancellationToken cancellationToken = default) { return(_storage.DeleteFileAsync(file, ToPrefixedTag(tag), cancellationToken)); }
/// <summary> /// Returns all files associated with the tag prefixed with <see cref="Prefix"/>. /// </summary> /// <param name="tag">The tag the files should be associated when it is prefixed with <see cref="Prefix"/>.</param> /// <returns> /// An <see cref="IAsyncEnumerable{T}"/> containing zero or more files associated with the <paramref name="tag"/> /// prefixed with <see cref="Prefix"/>. /// </returns> public IAsyncEnumerable <IFile> GetFiles(AlphaNumericString tag) => _storage.GetFiles(ToPrefixedTag(tag));
/// <summary> /// Returns all the files associated with the given <paramref name="tag"/> from the underlying storage, /// iff the storage has the permission <see cref="Permission.Read"/>. /// </summary> /// <param name="tag">The tag that the files should be associated with.</param> /// <returns> /// An <see cref="IAsyncEnumerable{T}"/> containing all the files associated with the given <paramref name="tag"/>. /// </returns> /// <exception cref="SecurityException">If the storage doesn't have the permission <see cref="Permission.Read"/>.</exception> public IAsyncEnumerable <IFile> GetFiles(AlphaNumericString tag) { ThrowIfNoPermission(Permission.Read); return(_storage.GetFiles(tag)); }
/// <summary> /// Moves the <paramref name="file"/> associated with the <paramref name="tag"/> when it is prefixed with <see cref="Prefix"/> /// to the <paramref name="destinationTag"/> that will be prefixed with <see cref="Prefix"/>. /// </summary> /// <param name="file">The file to reassociate with a new tag.</param> /// <param name="tag">The tag it is currently associated with.</param> /// <param name="destinationTag">The new tag the file should be associated with.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> /// <returns> /// A <see cref="Task"/> representing the asynchronous operation. /// </returns> public Task MoveFileAsync(IFileInfo file, AlphaNumericString tag, AlphaNumericString destinationTag, CancellationToken cancellationToken = default) { return(_storage.MoveFileAsync(file, ToPrefixedTag(tag), ToPrefixedTag(destinationTag), cancellationToken)); }
private AlphaNumericString ToPrefixedTag(AlphaNumericString tag) => Prefix + tag;
private bool IsPrefixedTag(AlphaNumericString tag) => tag.StartsWith(Prefix);
/// <summary> /// Deletes the given <paramref name="tag"/> from the underlying storage. The <paramref name="tag"/> will /// be prefixed with <see cref="Prefix"/> before queried at the underlying storage. /// </summary> /// <param name="tag">The tag that should be deleted. Will be prefixed.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> /// <returns> /// A flag indicating whether the tag was successfully deleted or not. /// </returns> public Task <bool> DeleteTagAsync(AlphaNumericString tag, CancellationToken cancellationToken = default) { return(_storage.DeleteTagAsync(ToPrefixedTag(tag), cancellationToken)); }
private string GetTagPath(AlphaNumericString tag) => Path.Combine(_rootPath, tag);
private AlphaNumericString FromPrefixedTag(AlphaNumericString prefixedTag) => prefixedTag.Substring(Prefix.Length);
private string GetFilePath(AlphaNumericString tag, IFileInfo file) => Path.Combine(GetTagPath(tag), file.FileName);
/// <summary> /// Deletes the given <paramref name="tag"/> from the underlying storage, iff the storage /// has the permission <see cref="Permission.Delete"/>. /// </summary> /// <param name="tag">The tag to delete.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> /// <returns> /// A <see cref="Task{TResult}"/> representing the asynchronous operation, containing /// a flag indicating whether the tag was successfully deleted or not. /// </returns> /// <exception cref="SecurityException">If the storage doesn't have the permission <see cref="Permission.Delete"/>.</exception> public Task <bool> DeleteTagAsync(AlphaNumericString tag, CancellationToken cancellationToken = default) { ThrowIfNoPermission(Permission.Delete); return(_storage.DeleteTagAsync(tag, cancellationToken)); }