| 1 | module main |
| 2 | |
| 3 | import os |
| 4 | import flag |
| 5 | import strings |
| 6 | |
| 7 | const tool_version = '0.0.4' |
| 8 | const tool_description = 'Converts a list of arbitrary files into a single v module file.' |
| 9 | |
| 10 | struct Context { |
| 11 | mut: |
| 12 | files []string |
| 13 | prefix string |
| 14 | show_help bool |
| 15 | module_name string |
| 16 | write_file string |
| 17 | } |
| 18 | |
| 19 | fn (context Context) header() string { |
| 20 | mut header_s := '' |
| 21 | header_s += 'module ${context.module_name}\n' |
| 22 | header_s += '\n' |
| 23 | allfiles := context.files.join(' ') |
| 24 | mut options := []string{} |
| 25 | if context.prefix.len > 0 { |
| 26 | options << '-p ${context.prefix}' |
| 27 | } |
| 28 | if context.module_name.len > 0 { |
| 29 | options << '-m ${context.module_name}' |
| 30 | } |
| 31 | if context.write_file.len > 0 { |
| 32 | options << '-w ${context.write_file}' |
| 33 | } |
| 34 | soptions := options.join(' ') |
| 35 | header_s += '// File generated by:\n' |
| 36 | header_s += '// v bin2v ${allfiles} ${soptions}\n' |
| 37 | header_s += '// Please, do not edit this file.\n' |
| 38 | header_s += '// Your changes may be overwritten.\n' |
| 39 | return header_s |
| 40 | } |
| 41 | |
| 42 | fn (context Context) footer() string { |
| 43 | return '' |
| 44 | } |
| 45 | |
| 46 | fn (context Context) file2v(bname string, fbytes []u8, _bn_max int) string { |
| 47 | mut sb := strings.new_builder(1000) |
| 48 | sb.write_string('const ${bname}_len' + ' = ${fbytes.len}\n') |
| 49 | fbyte := fbytes[0] |
| 50 | bnmae_line := 'const ${bname}' + ' = [u8(${fbyte}), ' |
| 51 | sb.write_string(bnmae_line) |
| 52 | mut line_len := bnmae_line.len + 3 |
| 53 | for i := 1; i < fbytes.len; i++ { |
| 54 | b := int(fbytes[i]).str() |
| 55 | if line_len > 98 { |
| 56 | sb.go_back(1) |
| 57 | sb.write_string('\n\t') |
| 58 | line_len = 8 |
| 59 | } |
| 60 | if i == fbytes.len - 1 { |
| 61 | sb.write_string(b) |
| 62 | line_len += b.len |
| 63 | } else { |
| 64 | sb.write_string('${b}, ') |
| 65 | line_len += b.len + 2 |
| 66 | } |
| 67 | } |
| 68 | sb.write_string(']!\n') |
| 69 | return sb.str() |
| 70 | } |
| 71 | |
| 72 | fn (context Context) bname_and_bytes(file string) !(string, []u8) { |
| 73 | fname := os.file_name(file) |
| 74 | fname_escaped := fname.replace_each(['.', '_', '-', '_']) |
| 75 | byte_name := '${context.prefix}${fname_escaped}'.to_lower() |
| 76 | fbytes := os.read_bytes(file) or { return error('Error: ${err.msg()}') } |
| 77 | return byte_name, fbytes |
| 78 | } |
| 79 | |
| 80 | fn (context Context) max_bname_len(bnames []string) int { |
| 81 | mut max := 0 |
| 82 | for n in bnames { |
| 83 | if n.len > max { |
| 84 | max = n.len |
| 85 | } |
| 86 | } |
| 87 | // Add 4 to max due to "_len" suffix |
| 88 | return max + 4 |
| 89 | } |
| 90 | |
| 91 | fn main() { |
| 92 | mut context := Context{} |
| 93 | mut fp := flag.new_flag_parser(os.args[1..]) |
| 94 | fp.application('v bin2v') |
| 95 | fp.version(tool_version) |
| 96 | fp.description(tool_description) |
| 97 | fp.arguments_description('FILE [FILE]...') |
| 98 | context.show_help = fp.bool('help', `h`, false, 'Show this help screen.') |
| 99 | context.module_name = fp.string('module', `m`, 'binary', 'Name of the generated module.') |
| 100 | context.prefix = fp.string('prefix', `p`, '', 'A prefix put before each resource name.') |
| 101 | context.write_file = fp.string('write', `w`, '', |
| 102 | 'Write directly to a file with the given name.') |
| 103 | if context.show_help { |
| 104 | println(fp.usage()) |
| 105 | exit(0) |
| 106 | } |
| 107 | files := fp.finalize() or { |
| 108 | eprintln('Error: ${err.msg()}') |
| 109 | exit(1) |
| 110 | } |
| 111 | real_files := files.filter(it != 'bin2v') |
| 112 | if real_files.len == 0 { |
| 113 | println(fp.usage()) |
| 114 | exit(0) |
| 115 | } |
| 116 | context.files = real_files |
| 117 | if context.write_file != '' && os.file_ext(context.write_file) !in ['.vv', '.v'] { |
| 118 | context.write_file += '.v' |
| 119 | } |
| 120 | mut file_byte_map := map[string][]u8{} |
| 121 | for file in real_files { |
| 122 | bname, fbytes := context.bname_and_bytes(file) or { |
| 123 | eprintln(err.msg()) |
| 124 | exit(1) |
| 125 | } |
| 126 | file_byte_map[bname] = fbytes |
| 127 | } |
| 128 | max_bname := context.max_bname_len(file_byte_map.keys()) |
| 129 | if context.write_file.len > 0 { |
| 130 | mut out_file := os.create(context.write_file)! |
| 131 | out_file.write_string(context.header())! |
| 132 | for bname, fbytes in file_byte_map { |
| 133 | out_file.write_string(context.file2v(bname, fbytes, max_bname))! |
| 134 | } |
| 135 | out_file.write_string(context.footer())! |
| 136 | } else { |
| 137 | print(context.header()) |
| 138 | for bname, fbytes in file_byte_map { |
| 139 | print(context.file2v(bname, fbytes, max_bname)) |
| 140 | } |
| 141 | print(context.footer()) |
| 142 | } |
| 143 | } |
| 144 | |