/// <summary>Moves the current directory to <paramref name="newAbsolutePath"/>.
        /// There is no effect on the physical prefix of the given directory, or on clients that already have the directory open.
        /// An error is raised if a directory already exists at `new_path`, or if the new path points to a child of the current directory.
        /// </summary>
        /// <param name="trans">Transaction to use for the operation</param>
        /// <param name="newAbsolutePath">Full path (from the root) where this directory will be moved. It must includes all the necessary layer ids (including the parents).</param>
        public async Task <FdbDirectorySubspace> MoveToAsync(IFdbTransaction trans, FdbPath newAbsolutePath)
        {
            Contract.NotNull(trans, nameof(trans));
            if (newAbsolutePath.IsEmpty)
            {
                throw new ArgumentNullException(nameof(newAbsolutePath));
            }
            EnsureIsValid();

            var partition = GetEffectivePartition();

            Contract.Assert(partition != null, "Effective partition cannot be null!");

            // verify that it is still inside the same partition
            var location = this.DirectoryLayer.VerifyPath(newAbsolutePath, "newAbsolutePath");

            if (!location.StartsWith(partition.Path))
            {
                throw new InvalidOperationException($"Cannot move between partitions ['{location}' is outside '{partition.Path}']");
            }

            var metadata = await this.DirectoryLayer.Resolve(trans);

            return((await metadata.MoveInternalAsync(trans, this.Descriptor.Path, location, throwOnError: true)) !);
        }
 /// <summary>Create a new location that points to the Directory Subspace at the given path.</summary>
 /// <param name="path">Absolute path of the target Directory Subspace</param>
 public FdbDirectorySubspaceLocation(FdbPath path)
 {
     if (!path.IsAbsolute)
     {
         throw new ArgumentException("Directory Subspace path must absolute.", nameof(path));
     }
     this.Path        = path;
     this.IsPartition = path.LayerId == FdbDirectoryPartition.LayerId;
 }
        /// <summary>Returns the list of all the subdirectories of a sub-directory.</summary>
        public async Task <List <FdbPath> > ListAsync(IFdbReadOnlyTransaction trans, FdbPath path = default)
        {
            Contract.NotNull(trans, nameof(trans));
            EnsureIsValid();

            var metadata = await this.DirectoryLayer.Resolve(trans);

            return((await metadata.ListInternalAsync(trans, ToAbsolutePath(path), throwIfMissing: true)) !);
        }
        /// <summary>Convert a path relative to this directory, into a path relative to the root of the current partition</summary>
        /// <param name="location">Path relative from this directory</param>
        /// <returns>Path relative to the path of the current partition</returns>
        protected virtual FdbPath ToAbsolutePath(FdbPath location)
        {
            if (location.IsAbsolute)
            {             // we only accept an absolute path if it is technically contained in the current directory
                if (!location.StartsWith(this.Descriptor.Path))
                {
                    throw new InvalidOperationException("Cannot use absolute path that is not contained within the current directory path.");
                }
                return(location);
            }

            return(this.Descriptor.Path.Add(location));
        }
        /// <summary>Creates a sub-directory with the given <paramref name="path"/> (creating intermediate subdirectories if necessary).
        /// An exception is thrown if the given sub-directory already exists.
        /// </summary>
        /// <param name="trans">Transaction to use for the operation</param>
        /// <param name="path">Relative path of the sub-directory to create. It must includes all the necessary layer ids (including the parents).</param>
        /// <returns></returns>
        public async Task <FdbDirectorySubspace> CreateAsync(IFdbTransaction trans, FdbPath path)
        {
            Contract.NotNull(trans, nameof(trans));
            if (path.IsEmpty)
            {
                throw new ArgumentNullException(nameof(path));
            }
            EnsureIsValid();

            var metadata = await this.DirectoryLayer.Resolve(trans).ConfigureAwait(false);

            return((await metadata.CreateOrOpenInternalAsync(null, trans, ToAbsolutePath(path), prefix: Slice.Nil, allowCreate: true, allowOpen: false, throwOnError: true).ConfigureAwait(false)) !);
        }
            /// <summary>List and open the sub-directories of the given directory</summary>
            /// <param name="tr">Transaction used for the operation</param>
            /// <param name="parent">Parent directory</param>
            /// <returns>Dictionary of all the sub directories of the <paramref name="parent"/> directory.</returns>
            public static async Task <Dictionary <string, FdbDirectorySubspace> > BrowseAsync(IFdbReadOnlyTransaction tr, IFdbDirectory parent)
            {
                Contract.NotNull(tr, nameof(tr));
                Contract.NotNull(parent, nameof(parent));

                // read the names of all the subdirectories
                var children = await parent.ListAsync(tr).ConfigureAwait(false);

                // open all the subdirectories
                var folders = await children
                              .ToAsyncEnumerable()
                              .SelectAsync((child, _) => parent.OpenAsync(tr, FdbPath.Relative(child.Name)))
                              .ToListAsync();

                // map the result
                return(folders.ToDictionary(ds => ds.Name));
            }
        /// <summary>Checks if a sub-directory exists</summary>
        /// <returns>Returns true if the directory exists, otherwise false.</returns>
        public async Task <bool> ExistsAsync(IFdbReadOnlyTransaction trans, FdbPath path)
        {
            Contract.NotNull(trans, nameof(trans));
            EnsureIsValid();

            // If path is empty, we are checking ourselves!
            var location = this.DirectoryLayer.VerifyPath(path, nameof(path));

            if (location.Count == 0)
            {
                return(await ExistsAsync(trans));
            }

            var metadata = await this.DirectoryLayer.Resolve(trans);

            return(await metadata.ExistsInternalAsync(trans, ToAbsolutePath(location)));
        }
        /// <summary>Attempts to remove a sub-directory, its contents, and all subdirectories.
        /// Warning: Clients that have already opened the directory might still insert data into its contents after it is removed.
        /// </summary>
        /// <param name="trans">Transaction to use for the operation</param>
        /// <param name="path">Path of the sub-directory to remove (relative to this directory)</param>
        public async Task <bool> TryRemoveAsync(IFdbTransaction trans, FdbPath path)
        {
            Contract.NotNull(trans, nameof(trans));
            EnsureIsValid();

            // If path is empty, we are removing ourselves!
            var location = this.DirectoryLayer.VerifyPath(path, nameof(path));

            if (location.Count == 0)
            {
                return(await TryRemoveAsync(trans));
            }

            var metadata = await this.DirectoryLayer.Resolve(trans);

            return(await metadata.RemoveInternalAsync(trans, ToAbsolutePath(location), throwIfMissing : false));
        }
        /// <summary>Removes a sub-directory, its contents, and all subdirectories.
        /// Warning: Clients that have already opened the directory might still insert data into its contents after it is removed.
        /// </summary>
        /// <param name="trans">Transaction to use for the operation</param>
        /// <param name="path">Path of the sub-directory to remove (relative to this directory)</param>
        public async Task RemoveAsync(IFdbTransaction trans, FdbPath path)
        {
            Contract.NotNull(trans, nameof(trans));
            EnsureIsValid();

            // If path is empty, we are removing ourselves!
            var location = this.DirectoryLayer.VerifyPath(path, nameof(path));

            if (location.Count == 0)
            {
                await RemoveAsync(trans).ConfigureAwait(false);
            }
            else
            {
                var metadata = await this.DirectoryLayer.Resolve(trans).ConfigureAwait(false);

                await metadata.RemoveInternalAsync(trans, ToAbsolutePath(location), throwIfMissing : true).ConfigureAwait(false);
            }
        }
            /// <summary>Opens a named partition, and change the root subspace of the database to the corresponding prefix</summary>
            internal static async Task SwitchToNamedPartitionAsync(FdbDatabase db, FdbPath root, CancellationToken ct)
            {
                Contract.Requires(db != null);
                ct.ThrowIfCancellationRequested();

                if (Logging.On)
                {
                    Logging.Verbose(typeof(Fdb.Directory), "OpenNamedPartitionAsync", $"Opened root layer using cluster file '{db.ClusterFile}'");
                }

                if (root.Count != 0)
                {
                    // create the root partition if does not already exist
                    var descriptor = await db.ReadWriteAsync(tr => db.DirectoryLayer.CreateOrOpenAsync(tr, root), ct).ConfigureAwait(false);

                    if (Logging.On)
                    {
                        Logging.Info(typeof(Fdb.Directory), "OpenNamedPartitionAsync", $"Opened partition {descriptor.Path} at {descriptor.GetPrefixUnsafe()}");
                    }
                }
            }
 /// <inheritdoc />
 public Task <bool> TryRemoveAsync(IFdbTransaction trans, FdbPath path = default)
 {
     return(trans.Context.Database.DirectoryLayer.TryRemoveAsync(trans, this.Path.Add(path)));
 }
        /// <summary>Attempts to move the specified sub-directory to <paramref name="newPath"/>.
        /// There is no effect on the physical prefix of the given directory, or on clients that already have the directory open.
        /// An error is raised if a directory already exists at `new_path`.
        /// </summary>
        /// <param name="trans">Transaction to use for the operation</param>
        /// <param name="oldPath">Relative path under this directory of the sub-directory to be moved</param>
        /// <param name="newPath">Relative path under this directory where the sub-directory will be moved to</param>
        /// <returns>Returns the directory at its new location if successful. If the directory cannot be moved, then null is returned.</returns>
        Task <FdbDirectorySubspace?> IFdbDirectory.TryMoveAsync(IFdbTransaction trans, FdbPath oldPath, FdbPath newPath)
        {
            if (oldPath.IsEmpty)
            {
                throw new ArgumentNullException(nameof(oldPath));
            }
            if (newPath.IsEmpty)
            {
                throw new ArgumentNullException(nameof(newPath));
            }
            EnsureIsValid();

            return(this.DirectoryLayer.TryMoveAsync(trans, ToAbsolutePath(oldPath), ToAbsolutePath(newPath)));
        }
        /// <summary>Attempts to move the current directory to <paramref name="newPath"/>.
        /// There is no effect on the physical prefix of the given directory, or on clients that already have the directory open.
        /// </summary>
        /// <param name="trans">Transaction to use for the operation</param>
        /// <param name="newPath">Full path (from the root) where this directory will be moved. It must includes all the necessary layer ids (including the parents).</param>
        public async Task <FdbDirectorySubspace?> TryMoveToAsync(IFdbTransaction trans, FdbPath newPath)
        {
            Contract.NotNull(trans, nameof(trans));
            if (newPath.IsEmpty)
            {
                throw new ArgumentNullException(nameof(newPath));
            }
            EnsureIsValid();

            var descriptor = this.Descriptor;

            var location = this.DirectoryLayer.VerifyPath(newPath, "newPath");

            if (!location.StartsWith(descriptor.Partition.Path))
            {
                throw ThrowHelper.InvalidOperationException("Cannot move between partitions.");
            }

            if (location.LayerId != this.Path.LayerId)
            {
                throw ThrowHelper.InvalidOperationException("Cannot change the layer id of a directory subspace while moving it to a new location.");
            }

            var metadata = await descriptor.DirectoryLayer.Resolve(trans).ConfigureAwait(false);

            return(await metadata.MoveInternalAsync(trans, descriptor.Path, location, throwOnError : false).ConfigureAwait(false));
        }
        /// <summary>Moves the specified sub-directory to <paramref name="newPath"/>.
        /// There is no effect on the physical prefix of the given directory, or on clients that already have the directory open.
        /// An error is raised if a directory already exists at `new_path`.
        /// </summary>
        /// <param name="trans">Transaction to use for the operation</param>
        /// <param name="oldPath">Relative path under this directory of the sub-directory to be moved</param>
        /// <param name="newPath">Relative path under this directory where the sub-directory will be moved to</param>
        /// <returns>Returns the directory at its new location if successful.</returns>
        async Task <FdbDirectorySubspace> IFdbDirectory.MoveAsync(IFdbTransaction trans, FdbPath oldPath, FdbPath newPath)
        {
            if (oldPath.IsEmpty)
            {
                throw new ArgumentNullException(nameof(oldPath));
            }
            if (newPath.IsEmpty)
            {
                throw new ArgumentNullException(nameof(newPath));
            }
            EnsureIsValid();

            var metadata = await this.DirectoryLayer.Resolve(trans);

            return((await metadata.MoveInternalAsync(trans, ToAbsolutePath(oldPath), ToAbsolutePath(newPath), throwOnError: true)) !);
        }
 /// <inheritdoc />
 public Task <FdbDirectorySubspace?> TryCreateAsync(IFdbTransaction trans, FdbPath subPath = default)
 {
     return(trans.Context.Database.DirectoryLayer.TryCreateAsync(trans, this.Path.Add(subPath)));
 }
 /// <inheritdoc />
 public Task <bool> ExistsAsync(IFdbReadOnlyTransaction trans, FdbPath path = default)
 {
     return(trans.Context.Database.DirectoryLayer.ExistsAsync(trans, this.Path.Add(path)));
 }
 /// <inheritdoc />
 public ValueTask <FdbDirectorySubspace?> TryOpenCachedAsync(IFdbReadOnlyTransaction trans, FdbPath path = default)
 {
     return(trans.Context.Database.DirectoryLayer.TryOpenCachedAsync(trans, this.Path.Add(path)));
 }
 /// <inheritdoc />
 public Task <FdbDirectorySubspace?> TryMoveToAsync(IFdbTransaction trans, FdbPath newAbsolutePath)
 {
     return(trans.Context.Database.DirectoryLayer.TryMoveAsync(trans, this.Path, newAbsolutePath));
 }
 /// <inheritdoc />
 Task <FdbDirectorySubspace?> IFdbDirectory.TryMoveAsync(IFdbTransaction trans, FdbPath oldPath, FdbPath newPath) => throw new NotSupportedException();
 /// <inheritdoc />
 Task <FdbDirectorySubspace> IFdbDirectory.RegisterAsync(IFdbTransaction trans, FdbPath subPath, Slice prefix) => throw new NotSupportedException();
 /// <inheritdoc />
 public Task <List <FdbPath>?> TryListAsync(IFdbReadOnlyTransaction trans, FdbPath path = default)
 {
     return(trans.Context.Database.DirectoryLayer.TryListAsync(trans, this.Path.Add(path)));
 }
        public async ValueTask <FdbDirectorySubspace?> TryOpenCachedAsync(IFdbReadOnlyTransaction trans, FdbPath path)
        {
            Contract.NotNull(trans, nameof(trans));
            if (path.IsEmpty)
            {
                throw new InvalidOperationException("Cannot open empty path");
            }

            EnsureIsValid();

            var metadata = await this.DirectoryLayer.Resolve(trans);

            return(await metadata.OpenCachedInternalAsync(trans, ToAbsolutePath(path), throwOnError : false));
        }
 /// <summary>Append a relative path to the current path</summary>
 public FdbDirectorySubspaceLocation this[FdbPath relativePath] => !relativePath.IsEmpty ? new FdbDirectorySubspaceLocation(this.Path.Add(relativePath)) : this;