From 3947c93e595e90560821c7930b3bc9d710ffcbc8 Mon Sep 17 00:00:00 2001 From: mik-tf Date: Sat, 5 Oct 2024 18:31:50 -0400 Subject: [PATCH] fmt file --- flist.v | 757 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 384 insertions(+), 373 deletions(-) diff --git a/flist.v b/flist.v index d8be717..a02ae20 100644 --- a/flist.v +++ b/flist.v @@ -4,484 +4,495 @@ import term import json import x.json2 -const ( - token_file = os.join_path(os.home_dir(), '.config', 'tfhubtoken') - binary_location = if os.user_os() == 'windows' { - 'C:\\Program Files\\flist\\flist.exe' - } else { - '/usr/local/bin/flist' - } -) +const token_file = os.join_path(os.home_dir(), '.config', 'tfhubtoken') +const binary_location = if os.user_os() == 'windows' { + 'C:\\Program Files\\flist\\flist.exe' +} else { + '/usr/local/bin/flist' +} struct FlistItem { - name string + name string } struct Payload { - username string + username string } struct Response { - payload Payload + payload Payload } fn error_message(msg string) { - println(term.red('\nError: ') + msg) - println(term.yellow('Run \'flist help\' for usage information.\n')) + println(term.red('\nError: ') + msg) + println(term.yellow("Run 'flist help' for usage information.\n")) } fn success_message(msg string) { - println(term.green('\n' + msg + '\n')) + println(term.green('\n' + msg + '\n')) } fn info_message(msg string) { - println(term.blue('\n' + msg + '\n')) + println(term.blue('\n' + msg + '\n')) } fn create_box(content []string, padding int) string { - mut max_width := 0 - for line in content { - clean_line := term.strip_ansi(line) - if clean_line.len > max_width { - max_width = clean_line.len - } - } - max_width += padding * 2 + mut max_width := 0 + for line in content { + clean_line := term.strip_ansi(line) + if clean_line.len > max_width { + max_width = clean_line.len + } + } + max_width += padding * 2 - separator := '━'.repeat(max_width + 2) // +2 for left and right borders - mut box_content := term.blue('┏$separator┓') + '\n' + separator := '━'.repeat(max_width + 2) // +2 for left and right borders + mut box_content := term.blue('┏${separator}┓') + '\n' - for line in content { - clean_line := term.strip_ansi(line) - padding_left := ' '.repeat(padding) - padding_right := ' '.repeat(max_width - clean_line.len) - box_content += term.blue('┃') + padding_left + line + padding_right + term.blue('┃') + '\n' - } + for line in content { + clean_line := term.strip_ansi(line) + padding_left := ' '.repeat(padding) + padding_right := ' '.repeat(max_width - clean_line.len) + box_content += term.blue('┃') + padding_left + line + padding_right + term.blue('┃') + + '\n' + } - box_content += term.blue('┗$separator┛') - return box_content + box_content += term.blue('┗${separator}┛') + return box_content } fn install() { - info_message('Installing Flist CLI...') - current_exe := os.executable() - if os.exists(current_exe) { - os.mkdir_all(os.dir(binary_location)) or { panic(err) } - os.cp(current_exe, binary_location) or { panic(err) } - os.chmod(binary_location, 0o755) or { panic(err) } - success_message('Flist CLI has been installed to ' + binary_location) - info_message('You can now use it by running \'flist help\'') - } else { - error_message('Cannot find the executable file') - exit(1) - } + info_message('Installing Flist CLI...') + current_exe := os.executable() + if os.exists(current_exe) { + os.mkdir_all(os.dir(binary_location)) or { panic(err) } + os.cp(current_exe, binary_location) or { panic(err) } + os.chmod(binary_location, 0o755) or { panic(err) } + success_message('Flist CLI has been installed to ' + binary_location) + info_message("You can now use it by running 'flist help'") + } else { + error_message('Cannot find the executable file') + exit(1) + } } fn uninstall() { - info_message('Uninstalling Flist CLI...') - if os.exists(binary_location) { - os.rm(binary_location) or { panic(err) } - success_message('Flist CLI has been removed from ' + binary_location) - } else { - info_message('Flist CLI is not installed at ' + binary_location) - } + info_message('Uninstalling Flist CLI...') + if os.exists(binary_location) { + os.rm(binary_location) or { panic(err) } + success_message('Flist CLI has been removed from ' + binary_location) + } else { + info_message('Flist CLI is not installed at ' + binary_location) + } } fn login() { - mut token_exists := os.exists(token_file) + mut token_exists := os.exists(token_file) - if !token_exists { - tfhub_token := os.input('Please enter your tfhub token: ') - os.write_file(token_file, tfhub_token) or { panic(err) } - success_message('Token saved in ' + token_file) - } else { - info_message('Your Flist Hub token is already saved.') - } + if !token_exists { + tfhub_token := os.input('Please enter your tfhub token: ') + os.write_file(token_file, tfhub_token) or { panic(err) } + success_message('Token saved in ' + token_file) + } else { + info_message('Your Flist Hub token is already saved.') + } - result := os.system('sudo docker login') - - if result == 0 { - info_message('\nYou are already logged in to Docker.') - } + result := os.system('sudo docker login') - success_message('Login process completed.') + if result == 0 { + info_message('\nYou are already logged in to Docker.') + } + + success_message('Login process completed.') } fn logout() { - if os.exists(token_file) { - os.rm(token_file) or {panic(err)} - success_message('Your Flist Hub Token has been removed') - } else { - info_message('Your Flist Hub Token was already not present.') - } + if os.exists(token_file) { + os.rm(token_file) or { panic(err) } + success_message('Your Flist Hub Token has been removed') + } else { + info_message('Your Flist Hub Token was already not present.') + } - exit_code := os.system('sudo docker logout') - if exit_code != 0 { - error_message('Failed to log out from Docker Hub.') - } + exit_code := os.system('sudo docker logout') + if exit_code != 0 { + error_message('Failed to log out from Docker Hub.') + } - success_message('You are now logged out of Docker Hub and your Flist Hub token has been removed.') + success_message('You are now logged out of Docker Hub and your Flist Hub token has been removed.') } fn get_docker_credential() !string { - // Try to get the Docker credential automatically - credential := get_docker_credential_auto() or { - // If automatic retrieval fails, prompt the user for input - println(term.yellow('\nCouldn\'t find your Docker username automatically.')) - username := os.input('Please enter your Docker username and press ENTER: ') - if username.trim_space() == '' { - return error('No Docker username provided') - } - return username.trim_space() - } - return credential + // Try to get the Docker credential automatically + credential := get_docker_credential_auto() or { + // If automatic retrieval fails, prompt the user for input + println(term.yellow("\nCouldn't find your Docker username automatically.")) + username := os.input('Please enter your Docker username and press ENTER: ') + if username.trim_space() == '' { + return error('No Docker username provided') + } + return username.trim_space() + } + return credential } fn get_docker_credential_auto() !string { - // First, try to get the Docker username using the system info command - system_info_result := os.execute('sudo docker system info | grep \'Username\' | cut -d \' \' -f 3') - if system_info_result.exit_code == 0 && system_info_result.output.trim_space() != '' { - return system_info_result.output.trim_space() - } + // First, try to get the Docker username using the system info command + system_info_result := os.execute("sudo docker system info | grep 'Username' | cut -d ' ' -f 3") + if system_info_result.exit_code == 0 && system_info_result.output.trim_space() != '' { + return system_info_result.output.trim_space() + } - // If the above method fails, proceed with the current method - // Read the Docker config file - config_path := os.join_path(os.home_dir(), '.docker', 'config.json') - config_content := os.read_file(config_path) or { - return error('Failed to read Docker config file: $err') - } + // If the above method fails, proceed with the current method + // Read the Docker config file + config_path := os.join_path(os.home_dir(), '.docker', 'config.json') + config_content := os.read_file(config_path) or { + return error('Failed to read Docker config file: ${err}') + } - // Parse the JSON content - config := json2.raw_decode(config_content) or { - return error('Failed to parse Docker config: $err') - } + // Parse the JSON content + config := json2.raw_decode(config_content) or { + return error('Failed to parse Docker config: ${err}') + } - // Extract the credsStore value - creds_store := config.as_map()['credsStore'] or { - return error('credsStore not found in Docker config') - }.str() + // Extract the credsStore value + creds_store := config.as_map()['credsStore'] or { + return error('credsStore not found in Docker config') + }.str() - // Execute the docker-credential command - cred_helper := 'docker-credential-$creds_store' - cred_output := os.execute('$cred_helper list') - if cred_output.exit_code != 0 { - return error('Failed to execute $cred_helper: ${cred_output.output}') - } + // Execute the docker-credential command + cred_helper := 'docker-credential-${creds_store}' + cred_output := os.execute('${cred_helper} list') + if cred_output.exit_code != 0 { + return error('Failed to execute ${cred_helper}: ${cred_output.output}') + } - // Parse the credential list - cred_list := json2.raw_decode(cred_output.output) or { - return error('Failed to parse credential list: $err') - } + // Parse the credential list + cred_list := json2.raw_decode(cred_output.output) or { + return error('Failed to parse credential list: ${err}') + } - // Find the first docker.io entry - for key, value in cred_list.as_map() { - if key.contains('docker.io') { - return value.str() - } - } + // Find the first docker.io entry + for key, value in cred_list.as_map() { + if key.contains('docker.io') { + return value.str() + } + } - return error('No docker.io credential found') + return error('No docker.io credential found') } fn push(tag string) { - docker_user := get_docker_credential() or { - error_message('Failed to get Docker username: $err') - exit(1) - } + docker_user := get_docker_credential() or { + error_message('Failed to get Docker username: ${err}') + exit(1) + } - info_message('Docker username: $docker_user') + info_message('Docker username: ${docker_user}') - full_tag := '${docker_user}/${tag}' + full_tag := '${docker_user}/${tag}' - tfhub_token := os.read_file(token_file) or { - error_message('No token found. Please run \'flist login\' first.') - exit(1) - } + tfhub_token := os.read_file(token_file) or { + error_message("No token found. Please run 'flist login' first.") + exit(1) + } - info_message('Starting Docker build') - if os.system('sudo docker buildx build -t ${full_tag} .') != 0 { - error_message('Docker build failed') - exit(1) - } + info_message('Starting Docker build') + if os.system('sudo docker buildx build -t ${full_tag} .') != 0 { + error_message('Docker build failed') + exit(1) + } - info_message('Finished local Docker build, now pushing to Docker Hub') - if os.system('sudo docker push ${full_tag}') != 0 { - error_message('Docker push failed') - exit(1) - } + info_message('Finished local Docker build, now pushing to Docker Hub') + if os.system('sudo docker push ${full_tag}') != 0 { + error_message('Docker push failed') + exit(1) + } - info_message('Converting Docker image to Flist now...') + info_message('Converting Docker image to Flist now...') - url := 'https://hub.grid.tf/api/flist/me/docker' - data := 'image=$full_tag' + url := 'https://hub.grid.tf/api/flist/me/docker' + data := 'image=${full_tag}' - mut config := http.FetchConfig{ - url: url - method: .post - data: data - header: http.new_header( - key: .authorization - value: 'bearer $tfhub_token' - ) - } + mut config := http.FetchConfig{ + url: url + method: .post + data: data + header: http.new_header( + key: .authorization + value: 'bearer ${tfhub_token}' + ) + } - config.header.add_custom('Content-Type', 'application/x-www-form-urlencoded') or { - error_message('Add custom failed: $err') - exit(1) - } + config.header.add_custom('Content-Type', 'application/x-www-form-urlencoded') or { + error_message('Add custom failed: ${err}') + exit(1) + } - response := http.fetch(config) or { - error_message('HTTP POST request failed: $err') - exit(1) - } + response := http.fetch(config) or { + error_message('HTTP POST request failed: ${err}') + exit(1) + } - if response.status_code == 200 { - hub_user := get_hub_username(tfhub_token) or { - error_message('Failed to get hub username') - exit(1) - } + if response.status_code == 200 { + hub_user := get_hub_username(tfhub_token) or { + error_message('Failed to get hub username') + exit(1) + } - flist_name := tag.replace(':', '-') + '.flist' - flist_url := 'https://hub.grid.tf/$hub_user/$flist_name' - - success_content := [ - term.bold(term.green('Success!') + ' Your Flist has been created and pushed to the TF Hub.'), - '', - term.bold('Flist Details:'), - term.yellow('Name: ') + flist_name, - term.yellow('User: ') + hub_user, - term.yellow('URL: ') + flist_url, - '', - 'You can access your Flist using the URL above.', - 'To manage your Flists, use the following commands:', - term.yellow(' flist ls ') + '- List all your Flists', - term.yellow(' flist delete') + '- Delete an Flist', - term.yellow(' flist rename') + '- Rename an Flist' - ] - - println(create_box(success_content, 2)) - } else { - error_message('Request failed with status code: ${response.status_code}') - println('Response body:') - println(response.body) - exit(1) - } + flist_name := tag.replace(':', '-') + '.flist' + flist_url := 'https://hub.grid.tf/${hub_user}/${flist_name}' + + success_content := [ + term.bold(term.green('Success!') + + ' Your Flist has been created and pushed to the TF Hub.'), + '', + term.bold('Flist Details:'), + term.yellow('Name: ') + flist_name, + term.yellow('User: ') + hub_user, + term.yellow('URL: ') + flist_url, + '', + 'You can access your Flist using the URL above.', + 'To manage your Flists, use the following commands:', + term.yellow(' flist ls ') + '- List all your Flists', + term.yellow(' flist delete') + '- Delete an Flist', + term.yellow(' flist rename') + '- Rename an Flist', + ] + + println(create_box(success_content, 2)) + } else { + error_message('Request failed with status code: ${response.status_code}') + println('Response body:') + println(response.body) + exit(1) + } } fn delete(flist_name string) { - tfhub_token := os.read_file(token_file) or { - error_message('No token found. Please run \'flist login\' first.') - exit(1) - } + tfhub_token := os.read_file(token_file) or { + error_message("No token found. Please run 'flist login' first.") + exit(1) + } - info_message('Deleting Flist: ' + flist_name) - url := 'https://hub.grid.tf/api/flist/me/' + flist_name - config := http.FetchConfig{ - url: url - method: .delete - header: http.new_header(key: .authorization, value: 'bearer ' + tfhub_token) - } + info_message('Deleting Flist: ' + flist_name) + url := 'https://hub.grid.tf/api/flist/me/' + flist_name + config := http.FetchConfig{ + url: url + method: .delete + header: http.new_header(key: .authorization, value: 'bearer ' + tfhub_token) + } - response := http.fetch(config) or { - error_message('Failed to send delete request: ' + err.msg()) - exit(1) - } + response := http.fetch(config) or { + error_message('Failed to send delete request: ' + err.msg()) + exit(1) + } - if response.status_code == 200 { - success_message('Deletion request sent successfully.') - } else { - error_message('Deletion request failed with status code: ' + response.status_code.str()) - } + if response.status_code == 200 { + success_message('Deletion request sent successfully.') + } else { + error_message('Deletion request failed with status code: ' + response.status_code.str()) + } } fn rename(flist_name string, new_flist_name string) { - tfhub_token := os.read_file(token_file) or { - error_message('No token found. Please run \'flist login\' first.') - exit(1) - } + tfhub_token := os.read_file(token_file) or { + error_message("No token found. Please run 'flist login' first.") + exit(1) + } - info_message('Renaming Flist: ' + flist_name + ' to ' + new_flist_name) - url := 'https://hub.grid.tf/api/flist/me/' + flist_name + '/rename/' + new_flist_name - config := http.FetchConfig{ - url: url - method: .get - header: http.new_header(key: .authorization, value: 'bearer ' + tfhub_token) - } + info_message('Renaming Flist: ' + flist_name + ' to ' + new_flist_name) + url := 'https://hub.grid.tf/api/flist/me/' + flist_name + '/rename/' + new_flist_name + config := http.FetchConfig{ + url: url + method: .get + header: http.new_header(key: .authorization, value: 'bearer ' + tfhub_token) + } - response := http.fetch(config) or { - error_message('Failed to send rename request: ' + err.msg()) - exit(1) - } + response := http.fetch(config) or { + error_message('Failed to send rename request: ' + err.msg()) + exit(1) + } - if response.status_code == 200 { - success_message('Rename request sent successfully.') - } else { - error_message('Rename request failed with status code: ' + response.status_code.str()) - } + if response.status_code == 200 { + success_message('Rename request sent successfully.') + } else { + error_message('Rename request failed with status code: ' + response.status_code.str()) + } } fn get_hub_username(tfhub_token string) ?string { - url := 'https://hub.grid.tf/api/flist/me' + url := 'https://hub.grid.tf/api/flist/me' - config := http.FetchConfig{ - url: url - method: .get - header: http.new_header( - key: .authorization - value: 'bearer $tfhub_token' - ) - } + config := http.FetchConfig{ + url: url + method: .get + header: http.new_header( + key: .authorization + value: 'bearer ${tfhub_token}' + ) + } - response := http.fetch(config) or { - error_message('Failed to fetch hub username: $err') - return none - } + response := http.fetch(config) or { + error_message('Failed to fetch hub username: ${err}') + return none + } - if response.status_code != 200 { - error_message('Failed to fetch hub username. Status code: $response.status_code') - return none - } + if response.status_code != 200 { + error_message('Failed to fetch hub username. Status code: ${response.status_code}') + return none + } - parsed_response := json.decode(Response, response.body) or { - error_message('Failed to parse JSON response: $err') - return none - } + parsed_response := json.decode(Response, response.body) or { + error_message('Failed to parse JSON response: ${err}') + return none + } - if parsed_response.payload.username != '' { - return parsed_response.payload.username - } + if parsed_response.payload.username != '' { + return parsed_response.payload.username + } - error_message('Username not found in response') - return none + error_message('Username not found in response') + return none } fn ls(show_url bool) { - tfhub_token := os.read_file(token_file) or { - error_message('No token found. Please run \'flist login\' first.') - exit(1) - } + tfhub_token := os.read_file(token_file) or { + error_message("No token found. Please run 'flist login' first.") + exit(1) + } - hub_user := get_hub_username(tfhub_token) or { - error_message('Failed to get hub username') - exit(1) - } + hub_user := get_hub_username(tfhub_token) or { + error_message('Failed to get hub username') + exit(1) + } - url := 'https://hub.grid.tf/api/flist/$hub_user' + url := 'https://hub.grid.tf/api/flist/${hub_user}' - config := http.FetchConfig{ - url: url - method: .get - header: http.new_header( - key: .authorization - value: 'bearer $tfhub_token' - ) - } + config := http.FetchConfig{ + url: url + method: .get + header: http.new_header( + key: .authorization + value: 'bearer ${tfhub_token}' + ) + } - response := http.fetch(config) or { - error_message('Failed to fetch data: $err') - exit(1) - } + response := http.fetch(config) or { + error_message('Failed to fetch data: ${err}') + exit(1) + } - if response.status_code != 200 { - error_message('Failed to fetch data. Status code: $response.status_code') - exit(1) - } + if response.status_code != 200 { + error_message('Failed to fetch data. Status code: ${response.status_code}') + exit(1) + } - data := json.decode([]FlistItem, response.body) or { - error_message('Failed to parse JSON: $err') - exit(1) - } + data := json.decode([]FlistItem, response.body) or { + error_message('Failed to parse JSON: ${err}') + exit(1) + } - mut content := [term.bold('Flists for user ' + term.green(hub_user) + ':')] - for item in data { - if show_url { - content << term.yellow('> ') + 'https://hub.grid.tf/' + hub_user + '/' + item.name - } else { - content << term.yellow('> ') + item.name - } - } + mut content := [term.bold('Flists for user ' + term.green(hub_user) + ':')] + for item in data { + if show_url { + content << term.yellow('> ') + 'https://hub.grid.tf/' + hub_user + '/' + item.name + } else { + content << term.yellow('> ') + item.name + } + } - println(create_box(content, 2)) + println(create_box(content, 2)) } fn help() { - welcome_msg := term.bold(term.green('Welcome to the Flist CLI!')) - println(create_box([welcome_msg], 2)) + welcome_msg := term.bold(term.green('Welcome to the Flist CLI!')) + println(create_box([welcome_msg], 2)) - println('This tool turns Dockerfiles and Docker images directly into Flists on the TF Flist Hub, passing by the Docker Hub.\n') - println(term.bold('Available commands:')) - println(term.blue(' install ') + '- Install the Flist CLI') - println(term.blue(' uninstall ') + '- Uninstall the Flist CLI') - println(term.blue(' login ') + '- Log in to Docker Hub and save the Flist Hub token') - println(term.blue(' logout ') + '- Log out of Docker Hub and remove the Flist Hub token') - println(term.blue(' push ') + '- Build and push a Docker image to Docker Hub, then convert and push it as an Flist to Flist Hub') - println(term.blue(' delete ') + '- Delete an Flist from Flist Hub') - println(term.blue(' rename ') + '- Rename an Flist in Flist Hub') - println(term.blue(' ls ') + '- List all Flists of the current user') - println(term.blue(' ls url ') + '- List all Flists of the current user with full URLs') - println(term.blue(' help ') + '- Display this help message\n') - println(term.bold('Usage:')) - println(term.yellow(' sudo ./flist install')) - println(term.yellow(' sudo flist uninstall')) - println(term.yellow(' flist login')) - println(term.yellow(' flist logout')) - println(term.yellow(' flist push :')) - println(term.yellow(' flist delete ')) - println(term.yellow(' flist rename ')) - println(term.yellow(' flist ls')) - println(term.yellow(' flist ls url')) - println(term.yellow(' flist help\n')) + println('This tool turns Dockerfiles and Docker images directly into Flists on the TF Flist Hub, passing by the Docker Hub.\n') + println(term.bold('Available commands:')) + println(term.blue(' install ') + '- Install the Flist CLI') + println(term.blue(' uninstall ') + '- Uninstall the Flist CLI') + println(term.blue(' login ') + '- Log in to Docker Hub and save the Flist Hub token') + println(term.blue(' logout ') + '- Log out of Docker Hub and remove the Flist Hub token') + println(term.blue(' push ') + + '- Build and push a Docker image to Docker Hub, then convert and push it as an Flist to Flist Hub') + println(term.blue(' delete ') + '- Delete an Flist from Flist Hub') + println(term.blue(' rename ') + '- Rename an Flist in Flist Hub') + println(term.blue(' ls ') + '- List all Flists of the current user') + println(term.blue(' ls url ') + '- List all Flists of the current user with full URLs') + println(term.blue(' help ') + '- Display this help message\n') + println(term.bold('Usage:')) + println(term.yellow(' sudo ./flist install')) + println(term.yellow(' sudo flist uninstall')) + println(term.yellow(' flist login')) + println(term.yellow(' flist logout')) + println(term.yellow(' flist push :')) + println(term.yellow(' flist delete ')) + println(term.yellow(' flist rename ')) + println(term.yellow(' flist ls')) + println(term.yellow(' flist ls url')) + println(term.yellow(' flist help\n')) } fn main() { - if os.args.len == 1 { - help() - return - } + if os.args.len == 1 { + help() + return + } - match os.args[1] { - 'install' { install() } - 'uninstall' { uninstall() } - 'push' { - if os.args.len == 3 { - push(os.args[2]) - } else { - error_message('Incorrect number of arguments for \'push\'.') - exit(1) - } - } - 'login' { login() } - 'logout' { logout() } - 'delete' { - if os.args.len == 3 { - delete(os.args[2]) - } else { - error_message('Incorrect number of arguments for \'delete\'.') - exit(1) - } - } - 'rename' { - if os.args.len == 4 { - rename(os.args[2], os.args[3]) - } else { - error_message('Incorrect number of arguments for \'rename\'.') - exit(1) - } - } - 'ls' { - if os.args.len == 2 { - ls(false) - } else if os.args.len == 3 && os.args[2] == 'url' { - ls(true) - } else { - error_message('Incorrect usage of \'ls\'. Use \'ls\' or \'ls url\'.') - exit(1) - } - } - 'help' { help() } - else { - error_message('Unknown command: ' + os.args[1]) - exit(1) - } - } -} \ No newline at end of file + match os.args[1] { + 'install' { + install() + } + 'uninstall' { + uninstall() + } + 'push' { + if os.args.len == 3 { + push(os.args[2]) + } else { + error_message("Incorrect number of arguments for 'push'.") + exit(1) + } + } + 'login' { + login() + } + 'logout' { + logout() + } + 'delete' { + if os.args.len == 3 { + delete(os.args[2]) + } else { + error_message("Incorrect number of arguments for 'delete'.") + exit(1) + } + } + 'rename' { + if os.args.len == 4 { + rename(os.args[2], os.args[3]) + } else { + error_message("Incorrect number of arguments for 'rename'.") + exit(1) + } + } + 'ls' { + if os.args.len == 2 { + ls(false) + } else if os.args.len == 3 && os.args[2] == 'url' { + ls(true) + } else { + error_message("Incorrect usage of 'ls'. Use 'ls' or 'ls url'.") + exit(1) + } + } + 'help' { + help() + } + else { + error_message('Unknown command: ' + os.args[1]) + exit(1) + } + } +}