| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- using Lua.IO;
- using Lua.Runtime;
- using Lua.Standard.Internal;
- namespace Lua.Standard;
- public class FileHandle : ILuaUserData
- {
- public static readonly LuaFunction IndexMetamethod = new("index", (context, ct) =>
- {
- context.GetArgument<FileHandle>(0);
- var key = context.GetArgument(1);
- if (key.TryRead<string>(out var name))
- {
- return new(context.Return(name switch
- {
- "close" => CloseFunction!,
- "flush" => FlushFunction!,
- "lines" => LinesFunction!,
- "read" => ReadFunction!,
- "seek" => SeekFunction!,
- "setvbuf" => SetVBufFunction!,
- "write" => WriteFunction!,
- _ => LuaValue.Nil
- }));
- }
- else
- {
- return new(context.Return(LuaValue.Nil));
- }
- });
- ILuaStream stream;
- public bool IsOpen => stream?.IsOpen ?? false;
- LuaTable? ILuaUserData.Metatable
- {
- get => fileHandleMetatable;
- set => fileHandleMetatable = value;
- }
- static LuaTable? fileHandleMetatable;
- static FileHandle()
- {
- fileHandleMetatable = new(0, 1);
- fileHandleMetatable[Metamethods.Index] = IndexMetamethod;
- fileHandleMetatable["__tostring"] = ToStringFunction;
- }
- public FileHandle(Stream stream, LuaFileOpenMode mode) : this(ILuaStream.CreateFromStream(stream, mode)) { }
- public FileHandle(ILuaStream stream)
- {
- this.stream = stream;
- }
- public ValueTask<double?> ReadNumberAsync(CancellationToken cancellationToken)
- {
- return stream.ReadNumberAsync(cancellationToken);
- }
- public ValueTask<string?> ReadLineAsync(bool keepEol, CancellationToken cancellationToken)
- {
- return stream.ReadLineAsync(keepEol, cancellationToken);
- }
- public ValueTask<string> ReadToEndAsync(CancellationToken cancellationToken)
- {
- return stream.ReadAllAsync(cancellationToken);
- }
- public ValueTask<string?> ReadStringAsync(int count, CancellationToken cancellationToken)
- {
- return stream.ReadAsync(count, cancellationToken);
- }
- public ValueTask WriteAsync(string content, CancellationToken cancellationToken)
- {
- return stream.WriteAsync(content.AsMemory(), cancellationToken);
- }
- public ValueTask WriteAsync(ReadOnlyMemory<char> content, CancellationToken cancellationToken)
- {
- return stream.WriteAsync(content, cancellationToken);
- }
- public long Seek(string whence, long offset)
- {
- return whence switch
- {
- "set" => stream.Seek(SeekOrigin.Begin, offset),
- "cur" => stream.Seek(SeekOrigin.Current, offset),
- "end" => stream.Seek(SeekOrigin.End, offset),
- _ => throw new ArgumentException($"Invalid option '{whence}'")
- };
- }
- public ValueTask FlushAsync(CancellationToken cancellationToken)
- {
- return stream.FlushAsync(cancellationToken);
- }
- public void SetVBuf(string mode, int size)
- {
- var bufferingMode = mode switch
- {
- "no" => LuaFileBufferingMode.NoBuffering,
- "full" => LuaFileBufferingMode.FullBuffering,
- "line" => LuaFileBufferingMode.LineBuffering,
- _ => throw new ArgumentException($"Invalid option '{mode}'")
- };
- stream.SetVBuf(bufferingMode, size);
- }
- public async ValueTask Close(CancellationToken cancellationToken)
- {
- if (!stream.IsOpen)
- throw new ObjectDisposedException(nameof(FileHandle));
- await stream.CloseAsync(cancellationToken);
- stream = null!;
- }
- static readonly LuaFunction CloseFunction = new("close", async (context, cancellationToken) =>
- {
- var file = context.GetArgument<FileHandle>(0);
- try
- {
- await file.Close(cancellationToken);
- return context.Return(true);
- }
- catch (IOException ex)
- {
- return context.Return(LuaValue.Nil, ex.Message, ex.HResult);
- }
- });
- static readonly LuaFunction FlushFunction = new("file.flush", async (context, cancellationToken) =>
- {
- var file = context.GetArgument<FileHandle>(0);
- try
- {
- await file.FlushAsync(cancellationToken);
- return context.Return(true);
- }
- catch (IOException ex)
- {
- return context.Return(LuaValue.Nil, ex.Message, ex.HResult);
- }
- });
- static readonly LuaFunction LinesFunction = new("file.lines", (context, cancellationToken) =>
- {
- var file = context.GetArgument<FileHandle>(0);
- var format = context.HasArgument(1)
- ? context.Arguments[1]
- : "*l";
- return new(context.Return(new CSharpClosure("iterator", [new(file), format], static async (context, cancellationToken) =>
- {
- var upValues = context.GetCsClosure()!.UpValues.AsMemory();
- var file = upValues.Span[0].Read<FileHandle>();
- context.Return();
- var resultCount = await IOHelper.ReadAsync(context.State, file, "file.lines", 0, upValues[1..], true, cancellationToken);
- return resultCount;
- })));
- });
- static readonly LuaFunction ReadFunction = new("file.read", async (context, cancellationToken) =>
- {
- var file = context.GetArgument<FileHandle>(0);
- var args = context.Arguments[1..].ToArray();
- context.Return();
- var resultCount = await IOHelper.ReadAsync(context.State, file, "file.read", 1, args, false, cancellationToken);
- return resultCount;
- });
- static readonly LuaFunction SeekFunction = new("file.seek", (context, cancellationToken) =>
- {
- var file = context.GetArgument<FileHandle>(0);
- var whence = context.HasArgument(1)
- ? context.GetArgument<string>(1)
- : "cur";
- var offset = context.HasArgument(2)
- ? context.GetArgument<int>(2)
- : 0;
- if (whence is not ("set" or "cur" or "end"))
- {
- throw new LuaRuntimeException(context.State, $"bad argument #2 to 'file.seek' (invalid option '{whence}')");
- }
- try
- {
- return new(context.Return(file.Seek(whence, (long)offset)));
- }
- catch (IOException ex)
- {
- return new(context.Return(LuaValue.Nil, ex.Message, ex.HResult));
- }
- });
- static readonly LuaFunction SetVBufFunction = new("file.setvbuf", (context, cancellationToken) =>
- {
- var file = context.GetArgument<FileHandle>(0);
- var mode = context.GetArgument<string>(1);
- var size = context.HasArgument(2)
- ? context.GetArgument<int>(2)
- : -1;
- file.SetVBuf(mode, size);
- return new(context.Return(true));
- });
- static readonly LuaFunction WriteFunction = new("file.write", async (context, cancellationToken) =>
- {
- var file = context.GetArgument<FileHandle>(0);
- var resultCount = await IOHelper.WriteAsync(file, "io.write", context with { ArgumentCount = context.ArgumentCount - 1 }, cancellationToken);
- return resultCount;
- });
- static readonly LuaFunction ToStringFunction = new("file.__tostring", (context, cancellationToken) =>
- {
- var file = context.GetArgument<FileHandle>(0);
- return new(context.Return($"file ({(file.IsOpen ? file.stream.GetHashCode() : "closed")})"));
- });
- }
|