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 = '