public async Task <T> GetObjectAsync <T>(ContentAddressableKey key, CancellationToken cancellationToken)
        {
            var bytes = await _ContentAddressableStore.TryGetObjectAsync(key, cancellationToken);

            if (bytes == null)
            {
                return(default);
        public IEnumerable <ContentAddressableKey> GetObjectKeys()
        {
            var objectsDirectory = new DirectoryInfo(_BasePath);

            DirectoryInfo[] contentDirectories;
            try
            {
                contentDirectories = objectsDirectory.GetDirectories();
            }
            catch (DirectoryNotFoundException)
            {
                yield break;
            }

            foreach (DirectoryInfo contentDirectory in contentDirectories)
            {
                foreach (FileInfo contentFile in contentDirectory.GetFiles())
                {
                    string hash = contentDirectory.Name + contentFile.Name;
                    var    key  = ContentAddressableKey.TryParse(hash);
                    if (key.IsValid)
                    {
                        _Logger.LogDebug($"discovered content key '{key}'");
                        yield return(key);
                    }
                }
            }
        }
        public async Task <ContentAddressableKey> StoreObjectAsync(byte[] content, CancellationToken cancellationToken)
        {
            var key      = new ContentAddressableKey(_Hasher.Hash(content));
            var filePath = ComputeFilePath(key);

            Directory.CreateDirectory(Path.GetDirectoryName(filePath).NotNull());
            using (FileStream stream = File.Create(filePath))
            {
                await stream.WriteAsync(content, 0, content.Length, cancellationToken).NotNull();
            }

            return(key);
        }
        public Task DeleteObjectAsync(ContentAddressableKey key)
        {
            var filePath = ComputeFilePath(key);

            if (File.Exists(filePath))
            {
                try
                {
                    File.Delete(filePath);
                }
                catch (FileNotFoundException)
                {
                }
                catch (DirectoryNotFoundException)
                {
                }
            }

            return(Task.CompletedTask);
        }
        public async Task <byte[]> TryGetObjectAsync(ContentAddressableKey key, CancellationToken cancellationToken)
        {
            using (_Logger.LogScope(LogLevel.Trace, $"{nameof(ContentAddressableFileObjectStore)}.{nameof(TryGetObjectAsync)}"))
            {
                var filePath = ComputeFilePath(key);
                if (!File.Exists(filePath))
                {
                    _Logger.LogDebug($"file '{filePath}' not found, returning empty content for '{key}'");
                    return(null);
                }

                try
                {
                    using (var stream = File.OpenRead(filePath))
                    {
                        byte[] result   = new byte[stream.Length];
                        int    inResult = await stream.ReadAsync(result, 0, result.Length, cancellationToken).NotNull();

                        if (inResult != result.Length)
                        {
                            throw new InvalidOperationException("Unable to read entire content");
                        }

                        _Logger.LogDebug($"retrieved {result.Length.Bytes()} for key '{key}'");

                        return(result);
                    }
                }
                catch (FileNotFoundException)
                {
                    _Logger.LogDebug($"file '{filePath}' was not found, returning empty content for key '{key}'");
                    return(null);
                }
                catch (DirectoryNotFoundException)
                {
                    _Logger.LogDebug($"directory containing '{filePath}' was not found, returning empty content for key '{key}'");
                    return(null);
                }
            }
        }
 public void PutKey(string name, ContentAddressableKey key) => _KeyStore.PutKey(name, key);
 public Task DeleteObjectAsync(ContentAddressableKey key) => _ObjectStore.DeleteObjectAsync(key);
 public Task <byte[]> TryGetObjectAsync(ContentAddressableKey key, CancellationToken cancellationToken)
 => _ObjectStore.TryGetObjectAsync(key, cancellationToken);
 private string ComputeFilePath(ContentAddressableKey key) => Path.Combine(_BasePath, key.Hash.Substring(0, 2), key.Hash.Substring(2));
 public static Task <T> GetObjectAsync <T>([NotNull] this IContentAddressableRepository repository, ContentAddressableKey key)
 => repository.GetObjectAsync <T>(key, CancellationToken.None);