v / examples / archive / tar_gz_reader.v
165 lines · 150 sloc · 4.6 KB · e2e5cf8db56f3562c7baa735061690be936bdf3e
Raw
1import archive.tar
2import flag
3import net.http
4import os
5import term
6
7const default_url = 'https://github.com/vlang/v/archive/refs/tags/v0.1.3.tar.gz'
8
9@[heap]
10struct Context {
11 url string // Web starting with http:// or https://. Local starting with file:///
12 chunks bool // true: decompress with callback
13 debug int // print debug lines
14 max_blocks int // if max_blocks > 0 and is reached stops early.
15 filename string // if filename is found as a path of a data block, stops early.
16}
17
18fn (ctx &Context) read_last_block(mut read tar.Read) bool {
19 if ctx.max_blocks > 0 && ctx.max_blocks < read.get_block_number() {
20 read.stop_early = true
21 return true
22 }
23 return false
24}
25
26fn new_context() !&Context {
27 mut fp := flag.new_flag_parser(os.args)
28 fp.application('tar_gz_reader')
29 fp.version('0.0.20250721')
30 fp.description('Reads into memory selected sections of *.tar.gz. archives from https or home_dir.')
31 fp.skip_executable()
32 ctx := &Context{
33 url: fp.string('url', `u`, default_url,
34 'archive *.tar.gz URL, default(${default_url}). Start name with file:/// for local')
35 chunks: fp.bool('chunks', `c`, false,
36 'decompress with chunks to reduce RAM usage, default(false)')
37 debug: fp.int('debug', `d`, 0,
38 'prints blocks: 1=other, 2:+dirs, 3=+files, 4=+data, default(0=silent)')
39 max_blocks: fp.int('max_blocks', `m`, 0,
40 'maximum blocks to read, stop early. Default(0=read all)')
41 filename: fp.string('filename', `f`, '',
42 'filename content complete print, stop early. Default(empty means none)')
43 }
44 additional := fp.finalize()!
45 if additional.len > 0 {
46 println('unprocessed args ${additional.join_lines()}')
47 }
48 return ctx
49}
50
51// Downloader downloads a *.tar.gz using HTTP chunks
52struct Downloader {
53mut:
54 chunks int
55 data []u8
56}
57
58fn new_downloader(url string) !&Downloader {
59 mut downloader := &Downloader{}
60 params := http.DownloaderParams{
61 downloader: downloader
62 }
63 if url.starts_with('http://') || url.starts_with('https://') {
64 http.download_file_with_progress(url, '', params)!
65 } else if url.starts_with('file:///') {
66 path := '${os.home_dir()}/${url[8..]}'
67 println('path ${path}')
68 downloader.data = os.read_bytes(path)!
69 }
70 return downloader
71}
72
73fn (mut d Downloader) on_start(mut _request http.Request, _path string) ! {}
74
75fn (mut d Downloader) on_chunk(_request &http.Request, chunk []u8, _already_received u64, expected u64) ! {
76 if expected == 0 {
77 return
78 }
79 d.chunks++
80 d.data << chunk
81}
82
83fn (mut d Downloader) on_finish(_request &http.Request, _response &http.Response) ! {}
84
85struct FileReader implements tar.Reader {
86 ctx &Context
87mut:
88 filepath string
89 content []u8
90}
91
92fn (mut f FileReader) other_block(mut read tar.Read, details string) {
93 if f.ctx.read_last_block(mut read) {
94 return
95 }
96 if f.ctx.debug > 0 {
97 row := 'OTHER block:${read.get_block_number():6} ${read.get_special()} ${details} ${read.get_path()} '
98 println(term.colorize(term.bright_yellow, row))
99 }
100}
101
102fn (mut f FileReader) dir_block(mut read tar.Read, size u64) {
103 if f.ctx.read_last_block(mut read) {
104 return
105 }
106 if f.ctx.debug > 1 {
107 row := 'DIR block:${read.get_block_number():6} ${read.get_path()} size:${size}'
108 println(term.colorize(term.green, row))
109 }
110}
111
112fn (mut f FileReader) file_block(mut read tar.Read, size u64) {
113 if f.ctx.read_last_block(mut read) {
114 return
115 }
116 path := read.get_path()
117 if f.ctx.debug > 2 {
118 row := ' FILE block:${read.get_block_number():6} ${path} size:${size}'
119 println(term.colorize(term.bright_blue, row))
120 }
121 if f.ctx.filename != '' && f.filepath == '' && path.ends_with(f.ctx.filename) {
122 f.filepath = path
123 }
124}
125
126fn (mut f FileReader) data_block(mut read tar.Read, data []u8, pending int) {
127 if f.ctx.read_last_block(mut read) {
128 return
129 }
130 path := read.get_path()
131 if f.ctx.debug > 3 {
132 println(' DATA block:${read.get_block_number():6} ${path} len:${data.len} pend:${pending}')
133 }
134 if f.ctx.filename != '' {
135 if f.filepath == path {
136 f.content << data
137 if pending == 0 {
138 // our file of interest data is complete
139 read.stop_early = true
140 }
141 }
142 }
143}
144
145fn main() {
146 ctx := new_context()!
147 reader := FileReader{
148 ctx: ctx
149 }
150 mut untar := tar.new_untar(reader)
151 mut decompressor := tar.new_decompressor(untar)
152 downloader := new_downloader(ctx.url)!
153 if ctx.chunks {
154 decompressor.read_chunks(downloader.data)!
155 } else {
156 decompressor.read_all(downloader.data)!
157 }
158 println('-'.repeat(80))
159 println('Download: ${ctx.url} chunks:${downloader.chunks} bytes=${downloader.data.len}')
160 println('Untar: ${untar}')
161 println('Content: Path:${reader.filepath} bytes:${reader.content.len}')
162 println('-'.repeat(80))
163 println('${reader.content.bytestr()}')
164 println('-'.repeat(80))
165}
166