show count of test failures
[lines.love.git] / source_file.lua
blob3adab1fb05a25f19cc3c48a5bc3cef65c89ffd60
1 -- primitives for saving to file and loading from file
3 function file_exists(filename)
4 local infile = App.open_for_reading(filename)
5 if infile then
6 infile:close()
7 return true
8 else
9 return false
10 end
11 end
13 function load_from_disk(State)
14 local infile = App.open_for_reading(State.filename)
15 State.lines = load_from_file(infile)
16 if infile then infile:close() end
17 end
19 function load_from_file(infile)
20 local result = {}
21 if infile then
22 local infile_next_line = infile:lines() -- works with both Lua files and LÖVE Files (https://www.love2d.org/wiki/File)
23 while true do
24 local line = infile_next_line()
25 if line == nil then break end
26 if line == '```lines' then -- inflexible with whitespace since these files are always autogenerated
27 table.insert(result, load_drawing(infile_next_line))
28 else
29 table.insert(result, {mode='text', data=line})
30 end
31 end
32 end
33 if #result == 0 then
34 table.insert(result, {mode='text', data=''})
35 end
36 return result
37 end
39 function save_to_disk(State)
40 local outfile = App.open_for_writing(State.filename)
41 if outfile == nil then
42 error('failed to write to "'..State.filename..'"')
43 end
44 for _,line in ipairs(State.lines) do
45 if line.mode == 'drawing' then
46 store_drawing(outfile, line)
47 else
48 outfile:write(line.data)
49 outfile:write('\n')
50 end
51 end
52 outfile:close()
53 end
55 function load_drawing(infile_next_line)
56 local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}
57 while true do
58 local line = infile_next_line()
59 assert(line)
60 if line == '```' then break end
61 local shape = json.decode(line)
62 if shape.mode == 'freehand' then
63 -- no changes needed
64 elseif shape.mode == 'line' or shape.mode == 'manhattan' then
65 local name = shape.p1.name
66 shape.p1 = Drawing.find_or_insert_point(drawing.points, shape.p1.x, shape.p1.y, --[[large width to minimize overlap]] 1600)
67 drawing.points[shape.p1].name = name
68 name = shape.p2.name
69 shape.p2 = Drawing.find_or_insert_point(drawing.points, shape.p2.x, shape.p2.y, --[[large width to minimize overlap]] 1600)
70 drawing.points[shape.p2].name = name
71 elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
72 for i,p in ipairs(shape.vertices) do
73 local name = p.name
74 shape.vertices[i] = Drawing.find_or_insert_point(drawing.points, p.x,p.y, --[[large width to minimize overlap]] 1600)
75 drawing.points[shape.vertices[i]].name = name
76 end
77 elseif shape.mode == 'circle' or shape.mode == 'arc' then
78 local name = shape.center.name
79 shape.center = Drawing.find_or_insert_point(drawing.points, shape.center.x,shape.center.y, --[[large width to minimize overlap]] 1600)
80 drawing.points[shape.center].name = name
81 elseif shape.mode == 'deleted' then
82 -- ignore
83 else
84 print(shape.mode)
85 assert(false)
86 end
87 table.insert(drawing.shapes, shape)
88 end
89 return drawing
90 end
92 function store_drawing(outfile, drawing)
93 outfile:write('```lines\n')
94 for _,shape in ipairs(drawing.shapes) do
95 if shape.mode == 'freehand' then
96 outfile:write(json.encode(shape), '\n')
97 elseif shape.mode == 'line' or shape.mode == 'manhattan' then
98 local line = json.encode({mode=shape.mode, p1=drawing.points[shape.p1], p2=drawing.points[shape.p2]})
99 outfile:write(line, '\n')
100 elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
101 local obj = {mode=shape.mode, vertices={}}
102 for _,p in ipairs(shape.vertices) do
103 table.insert(obj.vertices, drawing.points[p])
105 local line = json.encode(obj)
106 outfile:write(line, '\n')
107 elseif shape.mode == 'circle' then
108 outfile:write(json.encode({mode=shape.mode, center=drawing.points[shape.center], radius=shape.radius}), '\n')
109 elseif shape.mode == 'arc' then
110 outfile:write(json.encode({mode=shape.mode, center=drawing.points[shape.center], radius=shape.radius, start_angle=shape.start_angle, end_angle=shape.end_angle}), '\n')
111 elseif shape.mode == 'deleted' then
112 -- ignore
113 else
114 print(shape.mode)
115 assert(false)
118 outfile:write('```\n')
121 -- for tests
122 function load_array(a)
123 local result = {}
124 local next_line = ipairs(a)
125 local i,line,drawing = 0, ''
126 while true do
127 i,line = next_line(a, i)
128 if i == nil then break end
129 --? print(line)
130 if line == '```lines' then -- inflexible with whitespace since these files are always autogenerated
131 --? print('inserting drawing')
132 i, drawing = load_drawing_from_array(next_line, a, i)
133 --? print('i now', i)
134 table.insert(result, drawing)
135 else
136 --? print('inserting text')
137 local line_info = {mode='text'}
138 line_info.data = line
139 table.insert(result, line_info)
142 if #result == 0 then
143 table.insert(result, {mode='text', data=''})
145 return result
148 function load_drawing_from_array(iter, a, i)
149 local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}
150 local line
151 while true do
152 i, line = iter(a, i)
153 assert(i)
154 --? print(i)
155 if line == '```' then break end
156 local shape = json.decode(line)
157 if shape.mode == 'freehand' then
158 -- no changes needed
159 elseif shape.mode == 'line' or shape.mode == 'manhattan' then
160 local name = shape.p1.name
161 shape.p1 = Drawing.find_or_insert_point(drawing.points, shape.p1.x, shape.p1.y, --[[large width to minimize overlap]] 1600)
162 drawing.points[shape.p1].name = name
163 name = shape.p2.name
164 shape.p2 = Drawing.find_or_insert_point(drawing.points, shape.p2.x, shape.p2.y, --[[large width to minimize overlap]] 1600)
165 drawing.points[shape.p2].name = name
166 elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
167 for i,p in ipairs(shape.vertices) do
168 local name = p.name
169 shape.vertices[i] = Drawing.find_or_insert_point(drawing.points, p.x,p.y, --[[large width to minimize overlap]] 1600)
170 drawing.points[shape.vertices[i]].name = name
172 elseif shape.mode == 'circle' or shape.mode == 'arc' then
173 local name = shape.center.name
174 shape.center = Drawing.find_or_insert_point(drawing.points, shape.center.x,shape.center.y, --[[large width to minimize overlap]] 1600)
175 drawing.points[shape.center].name = name
176 elseif shape.mode == 'deleted' then
177 -- ignore
178 else
179 print(shape.mode)
180 assert(false)
182 table.insert(drawing.shapes, shape)
184 return i, drawing
187 function is_absolute_path(path)
188 local os_path_separator = package.config:sub(1,1)
189 if os_path_separator == '/' then
190 -- POSIX systems permit backslashes in filenames
191 return path:sub(1,1) == '/'
192 elseif os_path_separator == '\\' then
193 if path:sub(2,2) == ':' then return true end -- DOS drive letter followed by volume separator
194 local f = path:sub(1,1)
195 return f == '/' or f == '\\'
196 else
197 error('What OS is this? LÖVE reports that the path separator is "'..os_path_separator..'"')
201 function is_relative_path(path)
202 return not is_absolute_path(path)
205 function dirname(path)
206 local os_path_separator = package.config:sub(1,1)
207 if os_path_separator == '/' then
208 -- POSIX systems permit backslashes in filenames
209 return path:match('.*/') or './'
210 elseif os_path_separator == '\\' then
211 return path:match('.*[/\\]') or './'
212 else
213 error('What OS is this? LÖVE reports that the path separator is "'..os_path_separator..'"')
217 function test_dirname()
218 check_eq(dirname('a/b'), 'a/', 'F - test_dirname')
219 check_eq(dirname('x'), './', 'F - test_dirname/current')
222 function basename(path)
223 local os_path_separator = package.config:sub(1,1)
224 if os_path_separator == '/' then
225 -- POSIX systems permit backslashes in filenames
226 return string.gsub(path, ".*/(.*)", "%1")
227 elseif os_path_separator == '\\' then
228 return string.gsub(path, ".*[/\\](.*)", "%1")
229 else
230 error('What OS is this? LÖVE reports that the path separator is "'..os_path_separator..'"')
234 function empty(h)
235 for _,_ in pairs(h) do
236 return false
238 return true