1 module aurorafw.core.io.filestream;
2 
3 import std.stdio;
4 
5 import aurorafw.core.io.stream;
6 
7 version (unittest) import aurorafw.unit.assertion;
8 
9 @safe
10 class FileStream : IStream
11 {
12 
13 	/**
14 	 * Constructs a FileStream with a given filename and
15 	 * mode.
16 	 *
17 	  * Examples:
18 	 * --------------------
19 	 * FileStream foo = new FileStream("tuna.txt", "rb");
20 	 * --------------------
21 	 */
22 	public this(string filename, string mode = "rb")
23 	{
24 		fd = File(filename, mode);
25 
26 		import std.algorithm.searching : canFind;
27 
28 		_writable = WRITABLE.canFind(mode);
29 		_readable = READABLE.canFind(mode);
30 	}
31 
32 	@property ulong length()
33 	{
34 		return fd.size;
35 	}
36 
37 	@property ulong tell()
38 	{
39 		return fd.tell;
40 	}
41 
42 	@property bool empty()
43 	{
44 		return (fd.size - fd.tell) <= 0;
45 	}
46 
47 	@property bool eof()
48 	{
49 		return empty();
50 	}
51 
52 	@property bool seekable()
53 	{
54 		return true;
55 	}
56 
57 	@property bool readable()
58 	{
59 		return _readable;
60 	}
61 
62 	@property bool writable()
63 	{
64 		return _writable;
65 	}
66 
67 	ulong skip(ulong n)
68 	{
69 		return seek(n, Seek.CUR);
70 	}
71 
72 	ulong seek(in long pos, in Seek origin = Seek.SET)
73 	{
74 		import std.stdio : SEEK_SET, SEEK_CUR, SEEK_END;
75 
76 		int orig;
77 		with (Seek) final switch (origin)
78 		{
79 		case SET:
80 			orig = SEEK_SET;
81 			break;
82 		case CUR:
83 			orig = SEEK_CUR;
84 			break;
85 		case END:
86 			orig = SEEK_END;
87 			break;
88 		}
89 
90 		fd.seek(pos, orig);
91 
92 		return tell;
93 	}
94 
95 	void write(in ubyte b)
96 	{
97 		fd.rawWrite([b]);
98 	}
99 
100 	void write(in ubyte[] b)
101 	{
102 		fd.rawWrite(b);
103 	}
104 
105 	ubyte read()
106 	{
107 		if ((fd.tell + ubyte.sizeof) > fd.size)
108 			throw new StreamException("Attempt reading outside of the stream");
109 
110 		ubyte[1] b;
111 		fd.rawRead(b);
112 
113 		return b[0];
114 	}
115 
116 	ubyte[] read(in size_t n)
117 	{
118 		import std.conv : to;
119 
120 		auto buf = new ubyte[(fd.tell + n > fd.size) ? (fd.size - fd.tell).to!size_t : n];
121 		fd.rawRead(buf);
122 
123 		return buf;
124 	}
125 
126 	ubyte[] data()
127 	{
128 		import std.conv : to;
129 
130 		size_t s = (fd.size - fd.tell).to!size_t;
131 		if (!s)
132 			return [];
133 
134 		auto ret = new ubyte[s];
135 		fd.rawRead(ret);
136 		return ret;
137 	}
138 
139 	private enum READABLE = [
140 			"r", "w+", "r+", "x+", "c+", "rb", "w+b", "r+b", "x+b", "c+b", "rt", "w+t", "r+t", "x+t", "c+t", "a+"
141 		];
142 
143 	private enum WRITABLE = [
144 			"w", "w+", "r+", "x+", "c+", "wb", "w+b", "r+b", "x+b", "c+b", "w+t", "r+t", "x+t", "c+t", "a", "a+"
145 		];
146 
147 	private immutable bool _readable;
148 	private immutable bool _writable;
149 
150 	private File fd;
151 }
152 
153 version (unittest)
154 {
155 	@safe
156 	private string unittest_deleteme_file()
157 	{
158 		import std.file;
159 		import std.path : buildPath;
160 		import std.uuid : randomUUID;
161 
162 		auto dm = buildPath(tempDir(), randomUUID.toString);
163 		std.file.write(dm, "abc");
164 
165 		return dm;
166 	}
167 }
168 
169 ///
170 /*@safe*/
171 @("File Stream: Readable stream")
172 unittest
173 {
174 	import std.file;
175 
176 	auto dm = unittest_deleteme_file;
177 	scope (exit)
178 		std.file.remove(dm);
179 
180 	FileStream fs = new FileStream(dm);
181 	unittest_readable_stream(fs);
182 	assertFalse(fs.writable);
183 }
184 
185 ///
186 /*@safe*/
187 @("File Stream: Writable stream")
188 unittest
189 {
190 	import std.file;
191 
192 	auto dm = unittest_deleteme_file;
193 	scope (exit)
194 		std.file.remove(dm);
195 
196 	FileStream fs = new FileStream(dm, "w");
197 	unittest_writable_stream(fs);
198 	assertFalse(fs.readable);
199 }
200 
201 ///
202 /*@safe*/
203 @("File Stream: Read & Write stream")
204 unittest
205 {
206 	import std.file;
207 
208 	auto dm = unittest_deleteme_file;
209 	FileStream fs = new FileStream(dm, "r+");
210 	scope (exit)
211 		std.file.remove(dm);
212 	unittest_readable_stream(fs);
213 	unittest_writable_stream(fs);
214 }