128 lines
4.3 KiB
C#
128 lines
4.3 KiB
C#
//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<Stream> OpenFileForReadAsync(string path, IScheduler scheduler)
|
|
{
|
|
return SafeOpenFileAsync(path, FileMode.Open, FileAccess.Read, FileShare.Read, scheduler);
|
|
}
|
|
|
|
public IObservable<Stream> OpenFileForWriteAsync(string path, IScheduler scheduler)
|
|
{
|
|
return SafeOpenFileAsync(path, FileMode.Create, FileAccess.Write, FileShare.None, scheduler);
|
|
}
|
|
|
|
public IObservable<Unit> CreateRecursive(string path)
|
|
{
|
|
CreateRecursive(new DirectoryInfo(path));
|
|
return Observable.Return(Unit.Default);
|
|
}
|
|
|
|
public IObservable<Unit> 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<Stream> SafeOpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share, IScheduler scheduler = null)
|
|
{
|
|
scheduler = scheduler ?? Scheduler.Default;
|
|
var ret = new AsyncSubject<Stream>();
|
|
|
|
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<Stream>().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<string> SplitFullPath(DirectoryInfo info)
|
|
{
|
|
var root = Path.GetPathRoot(info.FullName);
|
|
var components = new List<string>();
|
|
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;
|
|
}
|
|
}
|
|
} |