// A small command-line front end for the dialnumber library.
//
// Each subcommand parses its argument the same way the library would and prints
// a short report. The output is the contract the README records, so it is kept
// stable and plain.
///|
fn main {
let args = @env.args()
if args.length() < 2 {
print_usage()
return
}
let sub = args[1]
let arg = arg_at(args, 2)
let region = arg_at(args, 3)
match sub {
"parse" => run_parse(arg, region)
"info" => run_info(arg)
"possible" => run_possible(arg, region)
"valid" => run_valid(arg, region)
"format" => run_format(arg, region)
"types" => run_types(arg, region)
"region" => run_region(arg)
"cc" => run_cc(arg)
"tel" => run_tel(arg)
"match" => run_match(arg, region)
"compare" => run_compare(arg, region, arg_at(args, 4))
"as-you-type" => run_as_you_type(arg, region)
"list" => run_list()
_ => print_usage()
}
}
///|
/// The argument at `i`, or "" when the command line is shorter.
fn arg_at(args : Array[String], i : Int32) -> String {
if i < args.length() {
args[i]
} else {
""
}
}
///|
fn print_usage() -> Unit {
println(
"usage: dialnumber [args]\n" +
" parse [region] parse and report the parts\n" +
" possible [region] length check against the region's plan\n" +
" valid [region] rule check, region and number type\n" +
" format [region] the four renderings\n" +
" types [region] length candidates and the pattern verdict\n" +
" region the region a parsed number resolves to\n" +
" info calling code, prefixes and lengths\n" +
" cc just the calling code\n" +
" tel parse an RFC 3966 tel: URI\n" +
" match run the numbering-plan pattern engine\n" +
" compare [region] compare two telephone numbers\n" +
" as-you-type show every incremental rendering\n" +
" list every region code in the table",
)
}
///|
fn run_parse(input : String, region : String) -> Unit {
match @dialnumber.parse(input, region) {
None => println("parse: invalid input")
Some(pn) => {
println("country_code: \{pn.country_code}")
println("national_number: \{pn.national_number}")
println("e164: \{@dialnumber.format_e164(pn)}")
show_region(@dialnumber.region_of_number(pn))
}
}
}
///|
fn run_possible(input : String, region : String) -> Unit {
match @dialnumber.parse(input, region) {
None => println("possible: no (unreadable)")
Some(pn) => {
println("possible: \{yes_no(@dialnumber.is_possible(pn))}")
show_region(@dialnumber.region_of_number(pn))
}
}
}
///|
fn run_format(input : String, region : String) -> Unit {
match @dialnumber.parse(input, region) {
None => println("format: invalid input")
Some(pn) => {
println("e164: \{@dialnumber.format_e164(pn)}")
println("international: \{@dialnumber.format_international(pn)}")
let nat = match @dialnumber.format_national(pn, region) {
Some(v) => v
None => "(need a region)"
}
println("national: " + nat)
println("rfc3966: \{@dialnumber.format_rfc3966(pn)}")
}
}
}
///|
fn run_valid(input : String, region : String) -> Unit {
match @dialnumber.parse(input, region) {
None => println("valid: no (unreadable)")
Some(pn) => {
println("valid: \{yes_no(@dialnumber.is_valid(pn))}")
match @dialnumber.number_type(pn) {
Some(t) => println("type: \{t.to_string()}")
None => println("type: (none)")
}
show_region(@dialnumber.region_of_number(pn))
}
}
}
///|
fn run_match(pattern : String, text : String) -> Unit {
if pattern == "" {
println("match: need a pattern")
return
}
println("well_formed: \{yes_no(@dialnumber.pattern_well_formed(pattern))}")
println("matches: \{yes_no(@dialnumber.matches_pattern(pattern, text))}")
}
///|
fn run_types(input : String, region : String) -> Unit {
match @dialnumber.parse(input, region) {
None => println("types: invalid input")
Some(pn) => {
let possible : Array[String] = []
for t in @dialnumber.possible_types(pn) {
possible.push(t.to_string())
}
println("length candidates: \{join(possible, ",")}")
match @dialnumber.number_type(pn) {
Some(t) => println("pattern verdict: \{t.to_string()}")
None => println("pattern verdict: (none)")
}
}
}
}
///|
fn run_region(input : String) -> Unit {
match @dialnumber.parse(input, "") {
None =>
match @dialnumber.parse(input, "US") {
None => println("region: unresolved")
Some(pn) => show_region(@dialnumber.region_of_number(pn))
}
Some(pn) => show_region(@dialnumber.region_of_number(pn))
}
}
///|
fn show_region(code : String?) -> Unit {
match code {
Some(c) => println("region: \{c}")
None => println("region: unresolved")
}
}
///|
fn run_info(code : String) -> Unit {
match @dialnumber.region_name(code) {
None => println("region: unknown")
Some(name) => {
let canon = match @dialnumber.canonical_region_code(code) {
Some(c) => c
None => code
}
println("region: " + canon)
println("name: \{name}")
match @dialnumber.calling_code(code) {
Some(v) => println("calling_code: \{v}")
None => println("calling_code: (none)")
}
match @dialnumber.idd_prefix(code) {
Some(v) => println("idd: \{v}")
None => println("idd: (none)")
}
match @dialnumber.national_prefix(code) {
Some(v) => println("national_prefix: \{v}")
None => println("national_prefix: (none)")
}
match @dialnumber.possible_lengths(code) {
Some(lens) => {
let parts : Array[String] = []
for n in lens {
parts.push(n.to_string())
}
println("lengths: \{join(parts, ",")}")
}
None => println("lengths: (none)")
}
}
}
}
///|
fn run_cc(code : String) -> Unit {
match @dialnumber.calling_code(code) {
Some(v) => println("cc: \{v}")
None => println("cc: (unknown)")
}
}
///|
fn run_tel(uri : String) -> Unit {
match @dialnumber.parse_tel_uri_full(uri) {
None => println("tel: invalid uri")
Some(t) => {
println(
"global_number: \{if t.global_number == "" { "(none)" } else { t.global_number }}",
)
println(
"local_number: \{if t.local_number == "" { "(none)" } else { t.local_number }}",
)
println(
"phone_context: \{if t.phone_context == "" { "(none)" } else { t.phone_context }}",
)
println(
"extension: \{if t.extension == "" { "(none)" } else { t.extension }}",
)
println("isub: \{if t.isub == "" { "(none)" } else { t.isub }}")
match @dialnumber.resolve_tel_uri(t) {
None => println("resolved: no")
Some(pn) => println("resolved: \{@dialnumber.format_e164(pn)}")
}
}
}
}
///|
fn run_list() -> Unit {
println(join(@dialnumber.region_codes(), " "))
}
///|
fn run_compare(a : String, b : String, region : String) -> Unit {
match (@dialnumber.parse(a, region), @dialnumber.parse(b, region)) {
(Some(left), Some(right)) =>
println("match: " + @dialnumber.is_number_match(left, right).to_string())
_ => println("compare: invalid input")
}
}
///|
fn run_as_you_type(input : String, region : String) -> Unit {
let formatter = @dialnumber.AsYouTypeFormatter::new(region)
for digit in input {
println(digit.to_string() + " => " + formatter.input_digit(digit))
}
}
///|
fn yes_no(b : Bool) -> String {
if b {
"yes"
} else {
"no"
}
}
///|
fn join(items : Array[String], sep : String) -> String {
let buf = StringBuilder()
for i = 0; i < items.length(); i = i + 1 {
if i > 0 {
buf.write_string(sep)
}
buf.write_string(items[i])
}
buf.to_string()
}