Innovenergy_trunk/csharp/lib/SysTools/SysPath.cs

229 lines
6.1 KiB
C#

using System.Text;
using InnovEnergy.SysTools.Remote;
using InnovEnergy.SysTools.Utils;
using static System.IO.Path;
namespace InnovEnergy.SysTools;
public readonly struct SysPath
{
private readonly String _Path;
public static SysPath Root { get; } = "/";
public Boolean IsRelative => _Path.ElementAtOrDefault(0) == '.';
public Boolean IsAbsolute => _Path.ElementAtOrDefault(0) == '/';
public Boolean IsEnvRef => !_Path.Contains('/');
public Boolean IsRoot => _Path == "/";
public SysPath Parent => this / "..";
public String Head => _Path.AfterLast(DirectorySeparatorChar);
public String Tail => _Path.UntilLast(DirectorySeparatorChar);
public String Extension => _Path.AfterLast('.');
public SysPath(String path)
{
if (path.IsNullOrWhiteSpace())
throw new ArgumentException(nameof(path));
path = path
.Replace(@"\", "/")
.Trim()
.TrimEnd('/');
if (path.Length == 0) _Path = "/"; // root
else if (!path.Contains('/')) _Path = path; // env
else
{
_Path = Normalize(path);
}
}
public static SysPath FromString(String strPath) => new SysPath(strPath);
public static SysPath FromUri(String uriPath)
{
var uri = new Uri(uriPath);
return new SysPath(uri.AbsolutePath);
}
public SysPath ToRelative()
{
if (IsRelative)
return this;
if (IsAbsolute)
return $".{this}";
return $"./{this}";
}
public SysPath ToAbsolute()
{
if (IsAbsolute)
return this;
if (IsRelative)
return $"/{_Path.AfterFirst('/')}";
return $"/{this}";
}
public SysPath ToEnvRef()
{
if (IsEnvRef)
return this;
return $"/{_Path.AfterFirst('/')}";
}
private static String Normalize(String path)
{
if (path == "/")
return path;
var components = path.Split('/');
if (components.Length == 1)
return path; // envRef
var stack = new Stack<String>();
foreach (var comp in components)
{
if (comp == ".")
{
if (stack.Count == 0)
stack.Push(comp);
// otherwise just ignore it
}
else if (comp == "..")
{
if (stack.Count == 0 || stack.Peek() == "..")
stack.Push(comp);
else
{
if (stack.Count == 1) // one parent
{
if (stack.Peek() == ".")
{
stack.Pop();
stack.Push("..");
}
else if (stack.Peek() == "")
throw new Exception("Malformed path. Cannot ascend above root.");
else
stack.Pop();
}
else
{
stack.Pop();
}
}
}
else
{
stack.Push(comp);
}
}
var sb = new StringBuilder();
if (stack.Count == 0)
return ".";
foreach (var comp in stack.Reverse())
{
sb.Append(comp);
sb.Append("/");
}
if (sb.Length > 1)
sb.Remove(sb.Length - 1, 1);
return sb.ToString();
}
public SysPath RelativeTo(SysPath referencePath)
{
// TODO
if (IsRelative || referencePath.IsRelative )
throw new InvalidOperationException();
var refPath = referencePath._Path.Split();
var path = _Path.Split();
var prefixSize = Enumerable
.Zip(refPath, path, (l, r) => l == r)
.TakeWhile(e => e)
.Count();
var nUps = refPath.Length - prefixSize;
var dirs = Enumerable
.Repeat("..", nUps)
.Concat(path.Skip(prefixSize));
var relative = String.Join(DirectorySeparatorChar.ToString(), dirs);
return relative.IsNullOrEmpty() ? "." : relative;
}
public Boolean IsSubPathOf(SysPath referencePath)
{
// TODO: verify
if (this == referencePath)
return true;
if (!_Path.StartsWith(referencePath))
return false;
return _Path.Length > referencePath._Path.Length &&
_Path[referencePath._Path.Length] == DirectorySeparatorChar;
}
public SysPath Append(SysPath right)
{
if (right.IsAbsolute)
throw new ArgumentException("right path cannot be absolute", nameof(right));
return $"{_Path}/{right}";
}
public RemotePath At(SshHost host) => new RemotePath(host, this);
#region overrides
public override Boolean Equals(Object obj) => _Path.Equals(obj?.ToString());
public override Int32 GetHashCode() => _Path.GetHashCode();
public override String ToString() => _Path;
#endregion
#region operators
public static implicit operator String(SysPath sysPath) => sysPath._Path;
public static implicit operator SysPath(String path) => new SysPath(path);
public static SysPath operator /(SysPath left, SysPath right) => left.Append(right);
public static SysPath operator /(String left, SysPath right) => FromString(left).Append(right);
public static SysPath operator /(SysPath left, String right) => left.Append(right);
public static Boolean operator ==(SysPath left, SysPath right) => left._Path == right._Path;
public static Boolean operator !=(SysPath left, SysPath right) => left._Path != right._Path;
public static Boolean operator ==(SysPath left, String right) => left._Path == right;
public static Boolean operator !=(SysPath left, String right) => left._Path != right;
public static Boolean operator ==(String left, SysPath right) => left == right._Path;
public static Boolean operator !=(String left, SysPath right) => left != right._Path;
#endregion
}