module main import os import net.http import net.urllib import v.vmod import json import term struct ModuleVpmInfo { // id int name string url string vcs string nr_downloads int } @[params] struct ErrorOptions { details string verbose bool // is used to only output the error message if the verbose setting is enabled. } const vexe = os.quoted_path(os.getenv('VEXE')) const home_dir = os.home_dir() fn get_mod_vpm_info(name string) !ModuleVpmInfo { if name.len < 2 || (!name[0].is_digit() && !name[0].is_letter()) { return error('invalid module name `${name}`.') } mut errors := []string{} for url in vpm_server_urls { modurl := url + '/api/packages/${name}' verbose_println('Retrieving metadata for `${name}` from `${modurl}`...') r := http.get(modurl) or { errors << 'Http server did not respond to our request for `${modurl}`.' errors << 'Error details: ${err}' continue } if r.status_code == 404 || r.body.trim_space() == '404' { errors << 'Skipping module `${name}`, since `${url}` reported that `${name}` does not exist.' continue } if r.status_code != 200 { errors << 'Skipping module `${name}`, since `${url}` responded with ${r.status_code} http status code. Please try again later.' continue } s := r.body if s.len > 0 && s[0] != `{` { errors << 'Invalid json data' errors << s.trim_space().limit(100) + '...' continue } mod := json.decode(ModuleVpmInfo, s) or { errors << 'Skipping module `${name}`, since its information is not in json format.' continue } if '' == mod.url || '' == mod.name { errors << 'Skipping module `${name}`, since it is missing name or url information.' continue } vpm_log(@FILE_LINE, @FN, 'name: ${name}; mod: ${mod}') return mod } return error(errors.join_lines()) } fn get_ident_from_url(raw_url string) !(string, string) { url := urllib.parse(raw_url) or { return error('failed to parse module URL `${raw_url}`.') } publisher, mut name := url.path.trim_left('/').rsplit_once('/') or { if settings.vcs == .hg && raw_url.count(':') > 1 { return '', 'test_module' } return error('failed to retrieve module name for `${url}`.') } name = name.trim_string_right('.git') vpm_log(@FILE_LINE, @FN, 'raw_url: ${raw_url}; publisher: ${publisher}; name: ${name}') return publisher, name } fn get_name_from_url(raw_url string) !string { _, name := get_ident_from_url(raw_url)! return name } fn normalize_mod_path(path string) string { return path.replace('-', '_').to_lower() } fn get_all_modules() []string { url := get_working_server_url() r := http.get(url) or { vpm_error(err.msg(), verbose: true) exit(1) } if r.status_code != 200 { vpm_error('failed to search vpm.vlang.io.', details: 'Status code: ${r.status_code}') exit(1) } s := r.body mut read_len := 0 mut modules := []string{} for read_len < s.len { mut start_token := "' // get the start index of the module entry mut start_index := s.index_after(start_token, read_len) if start_index == -1 { start_token = '