module main
import os
import net.http
import net.urllib
import sync.pool
import v.vmod
import json
import term
import log
struct ModuleVpmInfo {
// id int
name string
url string
vcs string
nr_downloads int
}
pub struct ModuleDateInfo {
name string
mut:
outdated bool
exec_err bool
}
@[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_date_info(mut pp pool.PoolProcessor, idx int, wid int) &ModuleDateInfo {
mut result := &ModuleDateInfo{
name: pp.get_item[string](idx)
}
path := get_path_of_existing_module(result.name) or { return result }
vcs := vcs_used_in_dir(path) or { return result }
is_hg := vcs.cmd == 'hg'
mut outputs := []string{}
for step in vcs.args.outdated {
cmd := '${vcs.cmd} ${vcs.args.path} "${path}" ${step}'
res := os.execute(cmd)
if res.exit_code < 0 {
verbose_println('Error command: ${cmd}')
verbose_println('Error details:\n${res.output}')
result.exec_err = true
return result
}
if is_hg && res.exit_code == 1 {
result.outdated = true
return result
}
outputs << res.output
}
// vcs.cmd == 'git'
if !is_hg && outputs[1] != outputs[2] {
result.outdated = true
}
return result
}
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 {
return error('failed to retrieve module name for `${url}`.')
}
vpm_log(@FILE_LINE, @FN, 'raw_url: ${raw_url}; publisher: ${publisher}; name: ${name}')
name = if name.ends_with('.git') { name.replace('.git', '') } else { 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_outdated() ![]string {
installed := get_installed_modules()
mut outdated := []string{}
mut pp := pool.new_pool_processor(callback: get_mod_date_info)
pp.work_on_items(installed)
for res in pp.get_results[ModuleDateInfo]() {
if res.exec_err {
return error('failed to check the latest commits for `${res.name}`.')
}
if res.outdated {
outdated << res.name
}
}
return outdated
}
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 = '