2017-07-11 20:29:54 -05:00
// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2017-08-12 14:48:28 -05:00
#[ macro_use ]
extern crate clap ;
2019-01-13 15:00:26 -06:00
extern crate dirs ;
2019-05-26 09:32:56 -05:00
extern crate rustyline ;
2017-05-05 22:33:25 -05:00
extern crate ucglib ;
2019-05-24 15:37:37 -05:00
use std ::collections ::BTreeMap ;
2018-08-17 12:59:26 -05:00
use std ::error ::Error ;
2017-11-15 22:41:55 -06:00
use std ::fs ::File ;
use std ::io ;
2019-05-24 15:37:37 -05:00
use std ::io ::Read ;
2018-08-17 12:59:26 -05:00
use std ::path ::{ Path , PathBuf } ;
2017-11-15 22:41:55 -06:00
use std ::process ;
2017-08-12 14:48:28 -05:00
use ucglib ::build ;
2019-01-05 14:27:49 -06:00
use ucglib ::convert ::{ ConverterRegistry , ImporterRegistry } ;
2019-05-24 15:37:37 -05:00
use ucglib ::iter ::OffsetStrIter ;
use ucglib ::parse ::parse ;
2017-08-12 14:48:28 -05:00
2018-08-26 15:46:52 -05:00
fn do_flags < ' a , ' b > ( ) -> clap ::App < ' a , ' b > {
2017-08-12 14:48:28 -05:00
clap_app! (
ucg = >
( version : crate_version ! ( ) )
( author : crate_authors ! ( ) )
( about : " Universal Configuration Grammar compiler. " )
2018-11-26 21:38:00 -06:00
( @ arg nostrict : - - ( " no-strict " ) " Turn off strict checking. " )
2019-05-26 09:32:56 -05:00
( @ subcommand repl = >
( about : " Start the ucg repl for interactive evaluation. " )
)
2017-08-12 14:48:28 -05:00
( @ subcommand build = >
2018-08-15 18:54:08 -05:00
( about : " Build a list of ucg files. " )
2018-11-28 21:15:03 -06:00
( @ arg recurse : - r " Whether we should recurse in directories or not. " )
2018-08-17 12:59:26 -05:00
( @ arg INPUT : .. . " Input ucg files or directories to build. If not provided then build the contents of the current directory. " )
2017-08-12 14:48:28 -05:00
)
2018-08-24 19:47:15 -05:00
( @ subcommand test = >
( about : " Check a list of ucg files for errors and run test assertions. " )
2018-11-28 21:15:03 -06:00
( @ arg recurse : - r " Whether we should recurse or not. " )
2018-08-24 19:47:15 -05:00
( @ arg INPUT : .. . " Input ucg files or directories to run test assertions for. If not provided it will scan the current directory for files with _test.ucg " )
2018-08-17 12:59:26 -05:00
)
2019-05-24 15:37:37 -05:00
( @ subcommand fmt = >
( about : " Format ucg files automatically. " )
( @ arg recurse : - r " Whether we should recurse or not. " )
( @ arg indent : - i - - indent " How many spaces to indent by. Defaults to 4 " )
( @ arg INPUT : .. . " Input ucg files or directories to format " )
2019-10-02 20:55:12 -05:00
( @ arg write : - w - - overwrite " Whether to overwrite the with the formatted form " )
2019-05-24 15:37:37 -05:00
)
2018-08-17 12:59:26 -05:00
( @ subcommand converters = >
( about : " list the available converters " )
2019-03-25 20:23:50 -04:00
( @ arg converter : " Converter name to get help for. " )
2017-08-12 14:48:28 -05:00
)
2019-01-05 14:27:49 -06:00
( @ subcommand importers = >
( about : " list the available importers for includes " )
)
2018-12-14 16:43:43 -06:00
( @ subcommand env = >
( about : " Describe the environment variables ucg uses. " )
)
2018-08-26 15:46:52 -05:00
)
2017-08-12 14:48:28 -05:00
}
2019-09-02 17:22:58 -05:00
struct StdoutWrapper ( io ::Stdout ) ;
impl StdoutWrapper {
fn new ( ) -> Self {
Self ( io ::stdout ( ) )
}
}
impl io ::Write for StdoutWrapper {
fn write ( & mut self , buf : & [ u8 ] ) -> io ::Result < usize > {
self . 0. write ( buf )
}
fn flush ( & mut self ) -> io ::Result < ( ) > {
self . 0. flush ( )
}
}
impl Clone for StdoutWrapper {
fn clone ( & self ) -> Self {
Self ( io ::stdout ( ) )
}
2017-11-15 22:41:55 -06:00
}
2019-09-02 17:22:58 -05:00
struct StderrWrapper ( io ::Stderr ) ;
impl StderrWrapper {
fn new ( ) -> Self {
Self ( io ::stderr ( ) )
}
}
impl io ::Write for StderrWrapper {
fn write ( & mut self , buf : & [ u8 ] ) -> io ::Result < usize > {
self . 0. write ( buf )
}
fn flush ( & mut self ) -> io ::Result < ( ) > {
self . 0. flush ( )
}
}
impl Clone for StderrWrapper {
fn clone ( & self ) -> Self {
Self ( io ::stderr ( ) )
}
}
// TODO(jwall): Build sharable stdout stderr providers.
2019-08-28 19:11:09 -05:00
fn build_file < ' a > (
2018-12-13 19:03:22 -06:00
file : & ' a str ,
2018-08-17 12:59:26 -05:00
validate : bool ,
2018-11-26 21:38:00 -06:00
strict : bool ,
2018-12-13 19:03:22 -06:00
import_paths : & ' a Vec < PathBuf > ,
2019-09-02 17:22:58 -05:00
) -> Result < build ::FileBuilder < ' a , StdoutWrapper , StderrWrapper > , Box < dyn Error > > {
2018-11-28 20:11:34 -06:00
let mut file_path_buf = PathBuf ::from ( file ) ;
if file_path_buf . is_relative ( ) {
2019-01-23 20:56:59 -06:00
file_path_buf = std ::env ::current_dir ( ) ? . join ( file_path_buf ) ;
2018-11-23 12:50:47 -06:00
}
2019-09-02 17:22:58 -05:00
let out = StdoutWrapper ::new ( ) ;
let err = StderrWrapper ::new ( ) ;
2019-09-01 19:32:45 -05:00
let mut builder = build ::FileBuilder ::new ( std ::env ::current_dir ( ) ? , import_paths , out , err ) ;
2019-11-09 15:07:46 -06:00
builder . set_strict ( strict ) ;
2018-08-17 12:59:26 -05:00
if validate {
builder . enable_validate_mode ( ) ;
}
2019-01-23 20:56:59 -06:00
builder . build ( file_path_buf ) ? ;
2018-08-17 12:59:26 -05:00
if validate {
2019-09-01 19:32:45 -05:00
println! ( " {} " , builder . assert_summary ( ) ) ;
2018-08-17 12:59:26 -05:00
}
Ok ( builder )
}
2019-09-01 19:32:45 -05:00
fn do_validate ( file : & str , strict : bool , import_paths : & Vec < PathBuf > ) -> bool {
2018-08-17 22:05:06 -05:00
println! ( " Validating {} " , file ) ;
2019-08-28 19:11:09 -05:00
match build_file ( file , true , strict , import_paths ) {
2018-08-22 18:59:40 -05:00
Ok ( b ) = > {
2019-09-01 19:32:45 -05:00
if b . assert_results ( ) {
println! ( " File {} Pass \n " , file ) ;
} else {
println! ( " File {} Fail \n " , file ) ;
2018-08-22 18:59:40 -05:00
return false ;
2019-09-01 19:32:45 -05:00
}
2018-08-22 18:59:40 -05:00
}
2018-08-17 12:59:26 -05:00
Err ( msg ) = > {
2018-08-17 22:05:06 -05:00
eprintln! ( " Err: {} " , msg ) ;
2018-08-17 12:59:26 -05:00
return false ;
}
}
return true ;
}
2019-09-01 19:32:45 -05:00
fn do_compile ( file : & str , strict : bool , import_paths : & Vec < PathBuf > ) -> bool {
2019-06-20 19:23:57 -05:00
println! ( " Building {} " , file ) ;
2019-08-28 19:11:09 -05:00
let builder = match build_file ( file , false , strict , import_paths ) {
2019-06-20 19:23:57 -05:00
Ok ( builder ) = > builder ,
Err ( err ) = > {
eprintln! ( " {} " , err ) ;
return false ;
}
} ;
2019-08-28 19:11:09 -05:00
if builder . out . is_none ( ) {
2019-06-20 19:23:57 -05:00
eprintln! ( " Build results in no artifacts. " ) ;
}
2019-10-18 06:51:17 -05:00
return true ;
2019-06-20 19:23:57 -05:00
}
2019-08-28 19:11:09 -05:00
fn visit_ucg_files (
2018-08-17 12:59:26 -05:00
path : & Path ,
recurse : bool ,
validate : bool ,
2018-11-26 21:38:00 -06:00
strict : bool ,
2018-12-13 19:03:22 -06:00
import_paths : & Vec < PathBuf > ,
2019-01-05 13:27:51 -06:00
) -> Result < bool , Box < dyn Error > > {
2018-08-17 12:59:26 -05:00
let our_path = String ::from ( path . to_string_lossy ( ) ) ;
let mut result = true ;
2018-08-20 22:06:52 -05:00
let mut summary = String ::new ( ) ;
2018-08-17 12:59:26 -05:00
if path . is_dir ( ) {
2018-12-06 12:46:47 -06:00
let mut dir_iter = std ::fs ::read_dir ( path ) ? . peekable ( ) ;
2018-08-25 18:37:58 -05:00
loop {
let entry = match dir_iter . next ( ) {
Some ( e ) = > e ,
None = > {
break ;
}
} ;
2018-12-06 12:46:47 -06:00
let next_item = entry ? ;
2018-08-17 12:59:26 -05:00
let next_path = next_item . path ( ) ;
let path_as_string = String ::from ( next_path . to_string_lossy ( ) ) ;
if next_path . is_dir ( ) & & recurse {
2019-09-01 19:32:45 -05:00
if let Err ( e ) = visit_ucg_files ( & next_path , recurse , validate , strict , import_paths )
{
2018-11-06 18:45:03 -06:00
eprintln! ( " {} " , e ) ;
2018-08-17 12:59:26 -05:00
result = false ;
}
} else {
if validate & & path_as_string . ends_with ( " _test.ucg " ) {
2019-09-01 19:32:45 -05:00
if ! do_validate ( & path_as_string , strict , import_paths ) {
2018-08-17 12:59:26 -05:00
result = false ;
2018-08-20 22:06:52 -05:00
summary . push_str ( format! ( " {} - FAIL \n " , path_as_string ) . as_str ( ) )
} else {
summary . push_str ( format! ( " {} - PASS \n " , path_as_string ) . as_str ( ) )
2018-08-17 12:59:26 -05:00
}
2018-08-25 18:37:58 -05:00
} else if ! validate & & path_as_string . ends_with ( " .ucg " ) {
2019-09-01 19:32:45 -05:00
if ! do_compile ( & path_as_string , strict , import_paths ) {
2018-08-17 12:59:26 -05:00
result = false ;
}
}
}
}
} else if validate & & our_path . ends_with ( " _test.ucg " ) {
2019-08-28 19:11:09 -05:00
if ! do_validate ( & our_path , strict , import_paths ) {
2018-08-17 12:59:26 -05:00
result = false ;
2018-08-25 18:37:58 -05:00
summary . push_str ( format! ( " {} - FAIL \n " , our_path ) . as_str ( ) ) ;
2018-08-20 22:06:52 -05:00
} else {
2018-08-25 18:37:58 -05:00
summary . push_str ( format! ( " {} - PASS \n " , & our_path ) . as_str ( ) ) ;
2018-08-17 12:59:26 -05:00
}
2018-08-20 22:06:52 -05:00
} else if ! validate {
2019-08-28 19:11:09 -05:00
if ! do_compile ( & our_path , strict , import_paths ) {
2018-08-17 12:59:26 -05:00
result = false ;
}
}
2018-11-15 16:57:24 -06:00
if validate & & ! summary . is_empty ( ) {
2018-08-20 22:06:52 -05:00
println! ( " RESULTS: " ) ;
println! ( " {} " , summary ) ;
}
2018-08-17 12:59:26 -05:00
Ok ( result )
}
2019-09-01 19:32:45 -05:00
fn build_command ( matches : & clap ::ArgMatches , import_paths : & Vec < PathBuf > , strict : bool ) {
2018-08-26 15:46:52 -05:00
let files = matches . values_of ( " INPUT " ) ;
let recurse = matches . is_present ( " recurse " ) ;
let mut ok = true ;
if files . is_none ( ) {
let curr_dir = std ::env ::current_dir ( ) . unwrap ( ) ;
2019-09-01 19:32:45 -05:00
let ok = visit_ucg_files ( curr_dir . as_path ( ) , recurse , false , strict , import_paths ) ;
2018-08-26 15:46:52 -05:00
if let Ok ( false ) = ok {
process ::exit ( 1 )
2018-08-17 12:59:26 -05:00
}
2018-11-15 16:57:24 -06:00
process ::exit ( 0 ) ;
2018-08-26 15:46:52 -05:00
}
for file in files . unwrap ( ) {
let pb = PathBuf ::from ( file ) ;
2019-09-01 19:32:45 -05:00
if let Ok ( false ) = visit_ucg_files ( & pb , recurse , false , strict , import_paths ) {
2018-08-26 15:46:52 -05:00
ok = false ;
}
}
if ! ok {
process ::exit ( 1 )
}
}
2019-10-02 20:55:12 -05:00
fn fmt_file ( p : & Path , indent : usize , overwrite : bool ) -> std ::result ::Result < ( ) , Box < dyn Error > > {
2019-05-24 15:37:37 -05:00
let mut contents = String ::new ( ) ;
2019-10-02 20:55:12 -05:00
{
let mut f = File ::open ( p ) ? ;
f . read_to_string ( & mut contents ) ? ;
}
2019-05-24 15:37:37 -05:00
let mut comment_map = BTreeMap ::new ( ) ;
let stmts = parse ( OffsetStrIter ::new ( & contents ) , Some ( & mut comment_map ) ) ? ;
2019-10-02 20:55:12 -05:00
if overwrite {
let mut printer = ucglib ::ast ::printer ::AstPrinter ::new ( indent , File ::create ( p ) ? )
. with_comment_map ( & comment_map ) ;
printer . render ( & stmts ) ? ;
} else {
let mut printer = ucglib ::ast ::printer ::AstPrinter ::new ( indent , std ::io ::stdout ( ) )
. with_comment_map ( & comment_map ) ;
printer . render ( & stmts ) ? ;
}
2019-05-24 15:37:37 -05:00
Ok ( ( ) )
}
fn fmt_dir ( p : & Path , recurse : bool , indent : usize ) -> std ::result ::Result < ( ) , Box < dyn Error > > {
// TODO(jwall): We should handle this error more gracefully
// for the user here.
let dir_iter = std ::fs ::read_dir ( p ) ? . peekable ( ) ;
for entry in dir_iter {
let next_item = entry . unwrap ( ) ;
let path = next_item . path ( ) ;
if path . is_dir ( ) & & recurse {
fmt_dir ( & path , recurse , indent ) ? ;
} else {
2019-10-02 20:55:12 -05:00
fmt_file ( & path , indent , true ) ? ;
2019-05-24 15:37:37 -05:00
}
}
Ok ( ( ) )
}
fn fmt_command ( matches : & clap ::ArgMatches ) -> std ::result ::Result < ( ) , Box < dyn Error > > {
let files = matches . values_of ( " INPUT " ) ;
let recurse = matches . is_present ( " recurse " ) ;
2019-10-02 20:55:12 -05:00
let overwrite = matches . is_present ( " write " ) ;
2019-05-24 15:37:37 -05:00
let indent = match matches . value_of ( " indent " ) {
Some ( s ) = > s . parse ::< usize > ( ) ? ,
None = > 4 ,
} ;
let mut paths = Vec ::new ( ) ;
if files . is_none ( ) {
paths . push ( std ::env ::current_dir ( ) ? ) ;
} else {
for f in files . unwrap ( ) {
paths . push ( PathBuf ::from ( f ) ) ;
}
}
for p in paths {
if p . is_dir ( ) {
fmt_dir ( & p , recurse , indent ) ? ;
} else {
2019-10-02 20:55:12 -05:00
fmt_file ( & p , indent , overwrite ) ? ;
2019-05-24 15:37:37 -05:00
}
}
Ok ( ( ) )
}
2019-09-01 19:32:45 -05:00
fn test_command ( matches : & clap ::ArgMatches , import_paths : & Vec < PathBuf > , strict : bool ) {
2018-08-26 15:46:52 -05:00
let files = matches . values_of ( " INPUT " ) ;
let recurse = matches . is_present ( " recurse " ) ;
if files . is_none ( ) {
let curr_dir = std ::env ::current_dir ( ) . unwrap ( ) ;
2019-09-01 19:32:45 -05:00
let ok = visit_ucg_files ( curr_dir . as_path ( ) , recurse , true , strict , import_paths ) ;
2018-08-26 15:46:52 -05:00
if let Ok ( false ) = ok {
process ::exit ( 1 )
}
} else {
let mut ok = true ;
2018-08-17 12:59:26 -05:00
for file in files . unwrap ( ) {
let pb = PathBuf ::from ( file ) ;
2018-08-26 15:46:52 -05:00
//if pb.is_dir() {
2019-09-01 19:32:45 -05:00
if let Ok ( false ) = visit_ucg_files ( pb . as_path ( ) , recurse , true , strict , import_paths ) {
2018-08-17 12:59:26 -05:00
ok = false ;
2018-08-15 18:22:05 -05:00
}
}
2018-08-17 12:59:26 -05:00
if ! ok {
process ::exit ( 1 )
}
2018-08-26 15:46:52 -05:00
}
process ::exit ( 0 ) ;
}
2019-03-25 20:23:50 -04:00
fn converters_command ( matches : & clap ::ArgMatches , registry : & ConverterRegistry ) {
if let Some ( ref cname ) = matches . value_of ( " converter " ) {
let mut found = false ;
for ( name , c ) in registry . get_converter_list ( ) . iter ( ) {
if cname = = name {
println! ( " * {} " , name ) ;
println! ( " Description: {} " , c . description ( ) ) ;
println! ( " Output Extension: `. {} ` " , c . file_ext ( ) ) ;
println! ( " " ) ;
println! ( " {} " , c . help ( ) ) ;
found = true ;
}
}
if ! found {
println! ( " No such converter {} " , cname ) ;
process ::exit ( 1 ) ;
}
} else {
println! ( " Available converters: " ) ;
2018-08-26 15:46:52 -05:00
println! ( " " ) ;
2019-03-25 20:23:50 -04:00
for ( name , c ) in registry . get_converter_list ( ) . iter ( ) {
println! ( " * {} " , name ) ;
println! ( " Description: {} " , c . description ( ) ) ;
println! ( " Output Extension: `. {} ` " , c . file_ext ( ) ) ;
println! ( " " ) ;
}
2018-08-26 15:46:52 -05:00
}
}
2019-01-05 14:27:49 -06:00
fn importers_command ( registry : & ImporterRegistry ) {
println! ( " Available importers " ) ;
println! ( " " ) ;
for ( name , _importer ) in registry . get_importer_list ( ) . iter ( ) {
println! ( " - {} " , name ) ;
}
}
2018-12-14 16:43:43 -06:00
fn env_help ( ) {
println! (
2019-05-28 20:36:12 -05:00
include_str! ( " help/env.txt " ) ,
2018-12-14 16:43:43 -06:00
std ::env ::var ( " UCG_IMPORT_PATH " ) . unwrap_or ( String ::new ( ) )
) ;
}
2019-11-09 15:07:46 -06:00
fn do_repl ( import_paths : & Vec < PathBuf > , strict : bool ) -> std ::result ::Result < ( ) , Box < dyn Error > > {
2019-05-26 09:32:56 -05:00
let config = rustyline ::Config ::builder ( ) ;
let mut editor = rustyline ::Editor ::< ( ) > ::with_config (
config
. history_ignore_space ( true )
. history_ignore_dups ( false )
. build ( ) ,
) ;
let path_home = dirs ::home_dir ( ) . unwrap_or ( std ::env ::temp_dir ( ) ) ;
let config_home = std ::env ::var ( " XDG_CACHE_HOME " )
. unwrap_or_else ( | _ | format! ( " {} /.cache " , path_home . to_string_lossy ( ) ) ) ;
let mut config_home = PathBuf ::from ( config_home ) ;
config_home . push ( " ucg " ) ;
config_home . push ( " line_hist " ) ;
if editor . load_history ( & config_home ) . is_err ( ) {
eprintln! (
" No history file {} Continuing without history. " ,
config_home . to_string_lossy ( )
) ;
// introduce a scope so the file will get automatically closed after
{
let base_dir = config_home . parent ( ) . unwrap ( ) ;
if ! base_dir . exists ( ) {
if let Err ( e ) = std ::fs ::create_dir_all ( base_dir ) {
eprintln! ( " {} " , e ) ;
}
}
if let Err ( e ) = std ::fs ::File ::create ( & config_home ) {
eprintln! ( " {} " , e ) ;
}
}
}
2019-09-01 19:32:45 -05:00
let mut builder = build ::FileBuilder ::new (
std ::env ::current_dir ( ) ? ,
import_paths ,
2019-09-02 17:22:58 -05:00
StdoutWrapper ::new ( ) ,
StderrWrapper ::new ( ) ,
2019-09-01 19:32:45 -05:00
) ;
2019-11-09 15:07:46 -06:00
builder . set_strict ( strict ) ;
2019-09-03 18:38:06 -05:00
builder . repl ( editor , config_home ) ? ;
Ok ( ( ) )
2019-05-26 09:32:56 -05:00
}
2019-11-09 15:07:46 -06:00
fn repl ( import_paths : & Vec < PathBuf > , strict : bool ) {
if let Err ( e ) = do_repl ( import_paths , strict ) {
2019-05-26 09:32:56 -05:00
eprintln! ( " {} " , e ) ;
process ::exit ( 1 ) ;
}
}
2018-08-26 15:46:52 -05:00
fn main ( ) {
let mut app = do_flags ( ) ;
let app_matches = app . clone ( ) . get_matches ( ) ;
2019-08-28 19:11:09 -05:00
// FIXME(jwall): Do we want these to be shared or not?
2018-08-26 15:46:52 -05:00
let registry = ConverterRegistry ::make_registry ( ) ;
2018-12-13 19:03:22 -06:00
let mut import_paths = Vec ::new ( ) ;
2019-01-13 15:00:26 -06:00
if let Some ( mut p ) = dirs ::home_dir ( ) {
p . push ( " .ucg " ) ;
// Attempt to create directory if it doesn't exist.
if ! p . exists ( ) {
std ::fs ::create_dir ( & p ) . unwrap ( ) ;
}
import_paths . push ( p ) ;
}
2018-12-13 19:03:22 -06:00
if let Ok ( path_list_str ) = std ::env ::var ( " UCG_IMPORT_PATH " ) {
for p in std ::env ::split_paths ( & path_list_str ) {
import_paths . push ( p ) ;
}
}
2018-11-26 21:38:00 -06:00
let strict = if app_matches . is_present ( " nostrict " ) {
false
} else {
true
} ;
2019-09-02 17:22:58 -05:00
if let Some ( matches ) = app_matches . subcommand_matches ( " build " ) {
2019-08-28 19:11:09 -05:00
build_command ( matches , & import_paths , strict ) ;
2018-08-26 15:46:52 -05:00
} else if let Some ( matches ) = app_matches . subcommand_matches ( " test " ) {
2019-08-28 19:11:09 -05:00
test_command ( matches , & import_paths , strict ) ;
2019-03-25 20:23:50 -04:00
} else if let Some ( matches ) = app_matches . subcommand_matches ( " converters " ) {
converters_command ( matches , & registry )
2019-01-05 14:27:49 -06:00
} else if let Some ( _ ) = app_matches . subcommand_matches ( " importers " ) {
let registry = ImporterRegistry ::make_registry ( ) ;
importers_command ( & registry )
2018-12-14 16:43:43 -06:00
} else if let Some ( _ ) = app_matches . subcommand_matches ( " env " ) {
env_help ( )
2019-05-26 09:32:56 -05:00
} else if let Some ( _ ) = app_matches . subcommand_matches ( " repl " ) {
2019-11-09 15:07:46 -06:00
repl ( & import_paths , strict )
2019-05-24 15:37:37 -05:00
} else if let Some ( matches ) = app_matches . subcommand_matches ( " fmt " ) {
if let Err ( e ) = fmt_command ( matches ) {
eprintln! ( " {} " , e ) ;
2019-10-02 19:33:52 -05:00
process ::exit ( 1 ) ;
2019-05-24 15:37:37 -05:00
}
2018-08-26 15:46:52 -05:00
} else {
app . print_help ( ) . unwrap ( ) ;
2018-08-22 00:13:11 -05:00
println! ( " " ) ;
2017-08-12 14:48:28 -05:00
}
2017-05-05 22:33:25 -05:00
}