//From https://github.com/akavache/Akavache //Copyright (c) 2012 GitHub //TODO: Remove once netstandard support is added using Akavache; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reactive; using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Reflection; namespace Discord { public class FilesystemProvider : IFilesystemProvider { public IObservable OpenFileForReadAsync(string path, IScheduler scheduler) { return SafeOpenFileAsync(path, FileMode.Open, FileAccess.Read, FileShare.Read, scheduler); } public IObservable OpenFileForWriteAsync(string path, IScheduler scheduler) { return SafeOpenFileAsync(path, FileMode.Create, FileAccess.Write, FileShare.None, scheduler); } public IObservable CreateRecursive(string path) { CreateRecursive(new DirectoryInfo(path)); return Observable.Return(Unit.Default); } public IObservable Delete(string path) { return Observable.Start(() => File.Delete(path), Scheduler.Default); } public string GetDefaultRoamingCacheDirectory() { throw new NotSupportedException(); } public string GetDefaultSecretCacheDirectory() { throw new NotSupportedException(); } public string GetDefaultLocalMachineCacheDirectory() { throw new NotSupportedException(); } protected static string GetAssemblyDirectoryName() { var assemblyDirectoryName = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); Debug.Assert(assemblyDirectoryName != null, "The directory name of the assembly location is null"); return assemblyDirectoryName; } private static IObservable SafeOpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share, IScheduler scheduler = null) { scheduler = scheduler ?? Scheduler.Default; var ret = new AsyncSubject(); Observable.Start(() => { try { var createModes = new[] { FileMode.Create, FileMode.CreateNew, FileMode.OpenOrCreate, }; // NB: We do this (even though it's incorrect!) because // throwing lots of 1st chance exceptions makes debugging // obnoxious, as well as a bug in VS where it detects // exceptions caught by Observable.Start as Unhandled. if (!createModes.Contains(mode) && !File.Exists(path)) { ret.OnError(new FileNotFoundException()); return; } Observable.Start(() => new FileStream(path, mode, access, share, 4096, false), scheduler).Cast().Subscribe(ret); } catch (Exception ex) { ret.OnError(ex); } }, scheduler); return ret; } private static void CreateRecursive(DirectoryInfo info) { SplitFullPath(info).Aggregate((parent, dir) => { var path = Path.Combine(parent, dir); if (!Directory.Exists(path)) Directory.CreateDirectory(path); return path; }); } private static IEnumerable SplitFullPath(DirectoryInfo info) { var root = Path.GetPathRoot(info.FullName); var components = new List(); for (var path = info.FullName; path != root && path != null; path = Path.GetDirectoryName(path)) { var filename = Path.GetFileName(path); if (String.IsNullOrEmpty(filename)) continue; components.Add(filename); } components.Add(root); components.Reverse(); return components; } } }