tesses-vfs/Tesses.VirtualFilesystem.Ex.../Class1.cs

603 lines
28 KiB
C#

/*
Tesses.VirtualFilesystem a library for virtual filesystems in .NET
Copyright (C) 2023 Mike Nolan
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using Tesses.VirtualFilesystem;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using System.Collections.Generic;
using System.Text;
namespace Tesses.VirtualFilesystem.Extensions
{
public static class Extensions
{
public static StreamReader OpenText(this IVirtualFilesystem fs,UnixPath path,Encoding encoding)
{
Stream strm=fs.OpenRead(path);
return new StreamReader(strm,encoding,false,4096,false);
}
public static async Task<StreamReader> OpenTextAsync(this IVirtualFilesystem fs,UnixPath path,Encoding encoding)
{
Stream strm=await fs.OpenReadAsync(path);
return new StreamReader(strm,encoding,false,4096,false);
}
public static StreamReader OpenText(this IVirtualFilesystem fs,UnixPath path)
{
Stream strm = fs.OpenRead(path);
return new StreamReader(strm,Encoding.UTF8,false,4096,false);
}
public static async Task<StreamReader> OpenTextAsync(this IVirtualFilesystem fs,UnixPath path)
{
Stream strm = await fs.OpenReadAsync(path);
return new StreamReader(strm,Encoding.UTF8,false,4096,false);
}
public static StreamWriter CreateText(this IVirtualFilesystem fs,UnixPath path,Encoding encoding)
{
Stream strm = fs.Create(path);
return new StreamWriter(strm,encoding,4096,false);
}
public static async Task<StreamWriter> CreateTextAsync(this IVirtualFilesystem fs,UnixPath path,Encoding encoding)
{
Stream strm = await fs.CreateAsync(path);
return new StreamWriter(strm,encoding,4096,false);
}
public static StreamWriter CreateText(this IVirtualFilesystem fs,UnixPath path)
{
Stream strm = fs.Create(path);
return new StreamWriter(strm,Encoding.UTF8,4096,false);
}
public static async Task<StreamWriter> CreateTextAsync(this IVirtualFilesystem fs,UnixPath path)
{
Stream strm = await fs.CreateAsync(path);
return new StreamWriter(strm,Encoding.UTF8,4096,false);
}
public static void WriteAllText(this IVirtualFilesystem fs,UnixPath path,string text,Encoding encoding)
{
using(var sw=fs.CreateText(path,encoding))
sw.Write(text);
}
public static async Task WriteAllTextAsync(this IVirtualFilesystem fs,UnixPath path,string text,Encoding encoding)
{
using(var sw=await fs.CreateTextAsync(path,encoding))
await sw.WriteAsync(text);
}
public static void WriteAllText(this IVirtualFilesystem fs,UnixPath path,string text)
{
using(var sw=fs.CreateText(path,Encoding.UTF8))
sw.Write(text);
}
public static async Task WriteAllTextAsync(this IVirtualFilesystem fs,UnixPath path,string text)
{
using(var sw=await fs.CreateTextAsync(path,Encoding.UTF8))
await sw.WriteAsync(text);
}
public static string ReadAllText(this IVirtualFilesystem fs,UnixPath path,Encoding encoding)
{
using(var sr = fs.OpenText(path,encoding))
return sr.ReadToEnd();
}
public static async Task<string> ReadAllTextAsync(this IVirtualFilesystem fs,UnixPath path,Encoding encoding)
{
using(var sr = await fs.OpenTextAsync(path,encoding))
return await sr.ReadToEndAsync();
}
public static string ReadAllText(this IVirtualFilesystem fs,UnixPath path)
{
using(var sr = fs.OpenText(path,Encoding.UTF8))
return sr.ReadToEnd();
}
public static async Task<string> ReadAllTextAsync(this IVirtualFilesystem fs,UnixPath path)
{
using(var sr = await fs.OpenTextAsync(path,Encoding.UTF8))
return await sr.ReadToEndAsync();
}
public static void WriteAllBytes(this IVirtualFilesystem fs,UnixPath path,byte[] data)
{
using(var s = fs.Create(path))
StreamHelper.WriteAllBytes(s,data);
}
public static async Task WriteAllBytesAsync(this IVirtualFilesystem fs,UnixPath path,byte[] data,CancellationToken token=default)
{
using(var strm = await fs.CreateAsync(path,token))
{
using(MemoryStream ms = new MemoryStream(data))
await ms.CopyToAsync(strm,4096,token);
}
}
public static byte[] ReadAllBytes(this IVirtualFilesystem fs,UnixPath path)
{
using(var s = fs.OpenRead(path))
return StreamHelper.ReadAllBytes(s);
}
public static async Task<byte[]> ReadAllBytesAsync(this IVirtualFilesystem fs,UnixPath path,CancellationToken token=default)
{
using(var strm = await fs.OpenReadAsync(path,token))
{
MemoryStream ms=new MemoryStream();
await strm.CopyToAsync(ms,4096,token);
return ms.ToArray();
}
}
public static Stream OpenRead(this IVirtualFilesystem fs,UnixPath path)
{
return fs.Open(path,FileMode.Open,FileAccess.Read,FileShare.Read);
}
public static async Task<Stream> OpenReadAsync(this IVirtualFilesystem fs,UnixPath path,CancellationToken token=default)
{
return await fs.OpenAsync(path,FileMode.Open,FileAccess.Read,FileShare.Read,token);
}
public static Stream OpenWrite(this IVirtualFilesystem fs,UnixPath path)
{
return fs.Open(path,FileMode.OpenOrCreate,FileAccess.Write,FileShare.None);
}
public static async Task<Stream> OpenWriteAsync(this IVirtualFilesystem fs,UnixPath path,CancellationToken token=default)
{
return await fs.OpenAsync(path,FileMode.OpenOrCreate,FileAccess.Write,FileShare.None,token);
}
public static Stream Create(this IVirtualFilesystem fs,UnixPath path)
{
return fs.Open(path,FileMode.Create,FileAccess.ReadWrite,FileShare.None);
}
public static async Task<Stream> CreateAsync(this IVirtualFilesystem fs,UnixPath path,CancellationToken token=default)
{
return await fs.OpenAsync(path,FileMode.Create,FileAccess.ReadWrite,FileShare.None,token);
}
public static DirectoryPointer OpenDirectory(this IVirtualFilesystem fs,Environment.SpecialFolder specialFolder)
{
return new DirectoryPointer(fs,UnixPath.FromSpecialDirectory(specialFolder));
}
public static bool MoveFile(this IVirtualFilesystem srcfs,IVirtualFilesystem destfs,UnixPath path,IProgress<TransferProgress> progress=null,int bufferLength=4096,bool deleteDir=true)
{
return srcfs.MoveFile(destfs,path,path,progress,bufferLength);
}
public static bool MoveDirectory(this IVirtualFilesystem srcfs,IVirtualFilesystem destfs,UnixPath path,IProgress<TransferProgress> progress=null,int bufferLength=4096,bool deleteDir=true)
{
return srcfs.MoveDirectory(destfs,path,path,progress,bufferLength,deleteDir);
}
public static async Task<bool> MoveFileAsync(this IVirtualFilesystem srcfs,IVirtualFilesystem destfs,UnixPath path,CancellationToken token=default(CancellationToken),IProgress<TransferProgress> progress=null,int bufferLength=4096,bool deleteDir=true)
{
return await srcfs.MoveFileAsync(destfs,path,path,token,progress,bufferLength);
}
public static async Task<bool> MoveDirectoryAsync(this IVirtualFilesystem srcfs,IVirtualFilesystem destfs,UnixPath path,CancellationToken token=default(CancellationToken),IProgress<TransferProgress> progress=null,int bufferLength=4096,bool deleteDir=true)
{
return await srcfs.MoveDirectoryAsync(destfs,path,path,token,progress,bufferLength,deleteDir);
}
public static async Task<bool> MoveDirectoryAsync(this IVirtualFilesystem srcfs,IVirtualFilesystem destfs,UnixPath source,UnixPath destination,CancellationToken token=default(CancellationToken),IProgress<TransferProgress> progress=null,int bufferLength=4096,bool deleteDir=true)
{
DateTime laccess=DateTime.Now;
DateTime lwrite = DateTime.Now;
try{
laccess = await srcfs.GetLastAccessTimeAsync(source,token);
lwrite = await srcfs.GetLastWriteTimeAsync(source,token);
if(!await destfs.DirectoryExistsAsync(destination,token))
await destfs.CreateDirectoryAsync(destination,token);
await foreach(var directory in srcfs.EnumerateDirectoriesAsync(source,token))
{
if(token.IsCancellationRequested) return false;
var newPath = destination / directory.Name;
var res=await srcfs.MoveDirectoryAsync(destfs,directory,newPath,token,progress,bufferLength);
if(!res) return false;
if(token.IsCancellationRequested) return false;
}
await foreach(var file in srcfs.EnumerateFilesAsync(source,token))
{
if(token.IsCancellationRequested) return false;
var newPath = destination / file.Name;
var res=await srcfs.MoveFileAsync(destfs,file,newPath,token,progress,bufferLength);
if(!res) return false;
if(token.IsCancellationRequested) return false;
}
}catch(Exception ex)
{
_=ex;
return false;
}
if(token.IsCancellationRequested) return false;
await destfs.SetCreationTimeAsync(destination,DateTime.Now,token);
await destfs.SetLastWriteTimeAsync(destination,lwrite,token);
await destfs.SetLastAccessTimeAsync(destination,laccess,token);
if(deleteDir && !token.IsCancellationRequested)
await srcfs.DeleteDirectoryAsync(source,token);
return true;
}
public static async Task<bool> MoveFileAsync(this IVirtualFilesystem srcfs,IVirtualFilesystem destfs,UnixPath source,UnixPath destination,CancellationToken token=default(CancellationToken),IProgress<TransferProgress> progress=null,int bufferLength=4096)
{
var success=await srcfs.CopyFileAsync(destfs,source,destination,token,progress,bufferLength);
if(!token.IsCancellationRequested && success)
await srcfs.DeleteFileAsync(source,token);
return success;
}
public static bool MoveFile(this IVirtualFilesystem srcfs,IVirtualFilesystem destfs,UnixPath source,UnixPath destination,IProgress<TransferProgress> progress=null,int bufferLength=4096)
{
bool success= srcfs.CopyFile(destfs,source,destination,progress,bufferLength);
if(success)
srcfs.DeleteFile(source);
return success;
}
public static bool MoveDirectory(this IVirtualFilesystem srcfs,IVirtualFilesystem destfs,UnixPath source,UnixPath destination,IProgress<TransferProgress> progress=null,int bufferLength=4096,bool deleteDir=true)
{
DateTime laccess = DateTime.Now;
DateTime lwrite = DateTime.Now;
try{
laccess = srcfs.GetLastAccessTime(source);
lwrite = srcfs.GetLastWriteTime(source);
if(!destfs.DirectoryExists(destination))
destfs.CreateDirectory(destination);
foreach(var directory in srcfs.EnumerateDirectories(source))
{
var newPath = destination / directory.Name;
srcfs.MoveDirectory(destfs,directory,newPath,progress,bufferLength);
}
foreach(var file in srcfs.EnumerateFiles(source))
{
var newPath = destination / file.Name;
srcfs.MoveFile(destfs,file,newPath,progress,bufferLength);
}
}catch(Exception ex)
{
_=ex;
return false;
}
destfs.SetCreationTime(destination,DateTime.Now);
destfs.SetLastWriteTime(destination,lwrite);
destfs.SetLastAccessTime(destination,laccess);
if(deleteDir)
srcfs.DeleteDirectory(source);
return true;
}
public static UnixPath[] GetFiles(this IVirtualFilesystem fs,UnixPath path)
{
return fs.EnumerateFiles(path).ToArray();
}
public static UnixPath[] GetDirectories(this IVirtualFilesystem fs,UnixPath path)
{
return fs.EnumerateDirectories(path).ToArray();
}
public static async Task<UnixPath[]> GetFilesAsync(this IVirtualFilesystem fs,UnixPath path,CancellationToken token = default(CancellationToken))
{
return await fs.EnumerateFilesAsync(path,token).ToArrayAsync(token);
}
public static async Task<T[]> ToArrayAsync<T>(this IAsyncEnumerable<T> enumerator,CancellationToken token=default(CancellationToken))
{
List<T> paths=new List<T>();
await foreach(var item in enumerator)
{
if(token.IsCancellationRequested) return paths.ToArray();
paths.Add(item);
}
return paths.ToArray();
}
public static async Task<UnixPath[]> GetDirectoriesAsync(this IVirtualFilesystem fs,UnixPath path,CancellationToken token=default(CancellationToken))
{
return await fs.EnumerateDirectoriesAsync(path).ToArrayAsync(token);
}
public static async Task DeleteDirectoryAsync(this IVirtualFilesystem fs,UnixPath path,bool recursive,CancellationToken token=default(CancellationToken))
{
if(recursive)
{
await foreach(var dir in fs.EnumerateDirectoriesAsync(path,token))
{
if(token.IsCancellationRequested) return;
await fs.DeleteDirectoryAsync(dir,recursive,token);
}
await foreach(var file in fs.EnumerateFilesAsync(path,token))
{
if(token.IsCancellationRequested) return;
await fs.DeleteFileAsync(file,token);
}
}
}
public static void DeleteDirectory(this IVirtualFilesystem fs,UnixPath path,bool recursive)
{
if(recursive)
{
foreach(var dir in fs.EnumerateDirectories(path))
{
fs.DeleteDirectory(dir,true);
}
foreach(var file in fs.EnumerateFiles(path))
{
fs.DeleteFile(file);
}
}
fs.DeleteDirectory(path);
}
public static async Task<bool> CopyDirectoryAsync(this IVirtualFilesystem srcfs,IVirtualFilesystem destfs,UnixPath path,CancellationToken token=default(CancellationToken),IProgress<TransferProgress> progress=null,int bufferLength=4096)
{
return await srcfs.CopyDirectoryAsync(destfs,path,path,token,progress,bufferLength);
}
public static async Task<bool> CopyDirectoryAsync(this IVirtualFilesystem srcfs,UnixPath srcPath,UnixPath destPath,CancellationToken token=default(CancellationToken),IProgress<TransferProgress> progress=null,int bufferLength=4096)
{
return await srcfs.CopyDirectoryAsync(srcfs,srcPath,destPath,token,progress,bufferLength);
}
public static async Task<bool> CopyFileAsync(this IVirtualFilesystem srcfs,IVirtualFilesystem destfs,UnixPath path,CancellationToken token=default(CancellationToken),IProgress<TransferProgress> progress=null,int bufferLength=4096)
{
return await srcfs.CopyFileAsync(destfs,path,path,token,progress,bufferLength);
}
public static async Task<bool> CopyFileAsync(this IVirtualFilesystem srcfs,UnixPath source,UnixPath destination,CancellationToken token=default(CancellationToken),IProgress<TransferProgress> progress=null,int bufferLength=4096)
{
return await srcfs.CopyFileAsync(srcfs,source,destination,token,progress,bufferLength);
}
public static async Task<bool> CopyDirectoryAsync(this IVirtualFilesystem srcfs,IVirtualFilesystem destfs,UnixPath source,UnixPath destination,CancellationToken token = default(CancellationToken),IProgress<TransferProgress> progress=null,int bufferLength=4096)
{
DateTime laccess=DateTime.Now;
try{
laccess = await srcfs.GetLastAccessTimeAsync(source);
if(!await destfs.DirectoryExistsAsync(destination,token))
await destfs.CreateDirectoryAsync(destination,token);
await foreach(var directory in srcfs.EnumerateDirectoriesAsync(source,token))
{
if(token.IsCancellationRequested) return false;
var newPath = destination / directory.Name;
var res= await srcfs.CopyDirectoryAsync(destfs,directory,newPath,token,progress,bufferLength);
if(!res) return false;
}
await foreach(var file in srcfs.EnumerateFilesAsync(source,token))
{
if(token.IsCancellationRequested) return false;
var newPath = destination / file.Name;
var res= await srcfs.CopyFileAsync(destfs,file,newPath,token,progress,bufferLength);
if(!res) return false;
}
}catch(Exception ex)
{
_=ex;
return false;
}
await destfs.SetCreationTimeAsync(destination,DateTime.Now,token);
await destfs.SetLastWriteTimeAsync(destination,await srcfs.GetLastWriteTimeAsync(source,token),token);
await destfs.SetLastAccessTimeAsync(destination,laccess,token);
try
{
await srcfs.SetLastAccessTimeAsync(source,DateTime.Now,token);
}catch(Exception ex)
{
_=ex;
}
return true;
}
public static async Task<bool> CopyFileAsync(this IVirtualFilesystem srcfs, IVirtualFilesystem destfs,UnixPath source,UnixPath destination,CancellationToken token=default(CancellationToken),IProgress<TransferProgress> progress=null,int bufferLength=4096)
{
DateTime laccess=DateTime.Now;
try{
laccess= await srcfs.GetLastAccessTimeAsync(source,token);
if(await destfs.DirectoryExistsAsync(destination,token)) destination /= source.Name;
using(var src = await srcfs.OpenAsync(source,FileMode.Open,FileAccess.Read,FileShare.Read,token))
{
using(var dest = await destfs.OpenAsync(destination,FileMode.OpenOrCreate,FileAccess.Write,FileShare.None,token))
{
byte[] buffer = new byte[bufferLength];
int read=0;
long readTotal=0;
long length = src.Length;
do
{
if(token.IsCancellationRequested) return false;
read = await src.ReadAsync(buffer,0,buffer.Length,token);
readTotal += read;
if(token.IsCancellationRequested) return false;
await dest.WriteAsync(buffer,0,read,token);
if(token.IsCancellationRequested) return false;
progress?.Report(new TransferProgress(readTotal,length,source,destination));
}
while(read != 0);
}
}
}catch(Exception ex)
{
_=ex;
return false;
}
await destfs.SetCreationTimeAsync(destination,DateTime.Now,token);
await destfs.SetLastWriteTimeAsync(destination,await srcfs.GetLastWriteTimeAsync(source,token),token);
await destfs.SetLastAccessTimeAsync(destination,laccess,token);
try
{
await srcfs.SetLastAccessTimeAsync(source,DateTime.Now,token);
}catch(Exception ex)
{
_=ex;
}
return true;
}
public static bool CopyFile(this IVirtualFilesystem srcfs,IVirtualFilesystem destfs,UnixPath path,IProgress<TransferProgress> progress=null,int bufferLength=4096)
{
return srcfs.CopyFile(destfs,path,path,progress,bufferLength);
}
public static bool CopyFile(this IVirtualFilesystem srcfs,IVirtualFilesystem destfs,UnixPath source,UnixPath destination,IProgress<TransferProgress> progress=null,int bufferLength=4096)
{
DateTime laccess=DateTime.Now;
try{
laccess = srcfs.GetLastAccessTime(source);
if(destfs.DirectoryExists(destination)) destination /= source.Name;
using(var src = srcfs.Open(source,FileMode.Open,FileAccess.Read,FileShare.Read))
{
using(var dest = destfs.Open(destination,FileMode.OpenOrCreate,FileAccess.Write,FileShare.None))
{
byte[] buffer = new byte[bufferLength];
int read=0;
long readTotal=0;
long length = src.Length;
do
{
read = src.Read(buffer,0,buffer.Length);
readTotal += read;
dest.Write(buffer,0,read);
progress?.Report(new TransferProgress(readTotal,length,source,destination));
}
while(read != 0);
}
}
}catch(Exception ex)
{
_=ex;
return false;
}
destfs.SetCreationTime(destination,DateTime.Now);
destfs.SetLastWriteTime(destination,srcfs.GetLastWriteTime(source));
destfs.SetLastAccessTime(destination,laccess);
try
{
srcfs.SetLastAccessTime(source,DateTime.Now);
}catch(Exception ex)
{
_=ex;
}
return true;
}
public static bool CopyFile(this IVirtualFilesystem fs,UnixPath source,UnixPath dest,IProgress<TransferProgress> progress = null,int bufferLength=4096)
{
return fs.CopyFile(fs,source,dest,progress,bufferLength);
}
public static bool CopyDirectory(this IVirtualFilesystem fs,IVirtualFilesystem destfs,UnixPath path,IProgress<TransferProgress> progress=null,int bufferLength=4096)
{
return fs.CopyDirectory(destfs,path,path,progress,bufferLength);
}
public static bool CopyDirectory(this IVirtualFilesystem fs,IVirtualFilesystem destfs,UnixPath source,UnixPath destination,IProgress<TransferProgress> progress=null,int bufferLength=4096)
{
DateTime laccess=DateTime.Now;
try{
laccess = fs.GetLastAccessTime(source);
if(!destfs.DirectoryExists(destination))
destfs.CreateDirectory(destination);
foreach(var directory in fs.EnumerateDirectories(source))
{
var newPath = destination / directory.Name;
fs.CopyDirectory(destfs,directory,newPath,progress,bufferLength);
}
foreach(var file in fs.EnumerateFiles(source))
{
var newPath = destination / file.Name;
fs.CopyFile(destfs,file,newPath,progress,bufferLength);
}
}catch(Exception ex)
{
_=ex;
return false;
}
destfs.SetCreationTime(destination,DateTime.Now);
destfs.SetLastWriteTime(destination,fs.GetLastWriteTime(source));
destfs.SetLastAccessTime(destination,laccess);
try
{
fs.SetLastAccessTime(source,DateTime.Now);
}catch(Exception ex)
{
_=ex;
}
return true;
}
public static bool CopyDirectory(this IVirtualFilesystem fs,UnixPath source,UnixPath destination,IProgress<TransferProgress> progress=null,int bufferLength = 4096)
{
return fs.CopyDirectory(fs,source,destination,progress,bufferLength);
}
public static double DivideByWith2Digits(long value,long divBy)
{
return Math.Round((double)value / (double)divBy,2);
}
public static double DivideByPowWith2Digits(long value,int unitSz,int pow)
{
return DivideByWith2Digits(value,(long)Math.Pow(unitSz,pow));
}
public static string ToDataUnits(this long val,bool binary=true)
{
int unitSz = binary ? 1024 : 1000;
string bytesSuffix = binary ? "iB" : "B";
if(val < unitSz)
{
return $"{val} B";
}
else if(val < Math.Pow(unitSz,2))
{
return $"{DivideByPowWith2Digits(val,unitSz,1)} k{bytesSuffix}";
}
else if(val < Math.Pow(unitSz,3))
{
return $"{DivideByPowWith2Digits(val,unitSz,2)} M{bytesSuffix}";
}
else if(val < Math.Pow(unitSz,4))
{
return $"{DivideByPowWith2Digits(val,unitSz,3)} G{bytesSuffix}";
}
return $"{DivideByPowWith2Digits(val,unitSz,4)} T{bytesSuffix}";
}
}
public class TransferProgress
{
public bool HasFiles {get;private set;}
public UnixPath DestinationPath {get;private set;}
public UnixPath SourcePath {get;private set;}
public long CopiedBytes {get;private set;}
public long RemainingBytes {get{return Length - CopiedBytes;}}
public long Length {get;private set;}
public double ProgressRaw {get;private set;}
public int Progress {get{return (int)(Math.Floor((double)(Progress * 100)) % 101);}}
public TransferProgress(long offset,long length,UnixPath src,UnixPath dest)
{
SourcePath = src;
DestinationPath = dest;
HasFiles = true;
CopiedBytes = offset;
Length = length;
if(length != 0)
{
ProgressRaw = offset / length;
}else{
ProgressRaw=0;
}
}
public TransferProgress(long offset,long length) : this(offset,length,new UnixPath(),new UnixPath())
{
HasFiles=false;
}
public override string ToString()
{
return ToString(true);
}
public string ToString(bool binary)
{
return $"{Progress}% {CopiedBytes.ToDataUnits(binary)}/{Length.ToDataUnits(binary)}";
}
}
}