@ -1,60 +1,85 @@
use crate ::project_dirs ;
use anyhow ::* ;
use derive_more ::FromStr ;
use log ::* ;
use schemars ::JsonSchema ;
use serde ::{ Deserialize , Serialize } ;
use std ::ffi ::OsString ;
use std ::{ iter ::IntoIterator , str ::FromStr } ;
use std ::{ fs ::File , io ::Write , iter ::IntoIterator , str ::FromStr } ;
use structopt ::StructOpt ;
#[ derive(Debug, Deserialize, Serialize) ]
struct ReadableBytesCount ( i64 ) ;
fn parse_readable_bytes_str ( s : & str ) -> Result < i64 , Error > {
let suffix = s . chars ( ) . last ( ) ;
if let Some ( suffix ) = suffix {
match suffix {
'k' | 'M' | 'G' = > i64 ::from_str ( s . trim_end_matches ( suffix ) )
. with_context ( | | format! ( "Could not parse int" ) )
. map ( | e | {
e * match suffix {
'k' = > 1000 ,
'M' = > 1000_000 ,
'G' = > 1000_000_000 ,
_ = > panic! ( "impossible" ) ,
}
} ) ,
_ = > i64 ::from_str ( s ) . with_context ( | | format! ( "Could not parse int" ) ) ,
}
} else {
Err ( format_err ! ( "empty byte input" ) )
fn is_default < T : Default + PartialEq > ( t : & T ) -> bool {
t = = & T ::default ( )
}
#[ derive(JsonSchema, Debug, Serialize, Deserialize, Copy, Clone, PartialEq, FromStr) ]
pub struct CacheCompressionLevel ( pub i32 ) ;
impl ToString for CacheCompressionLevel {
fn to_string ( & self ) -> String {
self . 0. to_string ( )
}
}
impl Default for CacheCompressionLevel {
fn default ( ) -> Self {
CacheCompressionLevel ( 12 )
}
}
#[ derive(JsonSchema, Debug, Serialize, Deserialize, Copy, Clone, PartialEq, FromStr) ]
pub struct MaxArchiveRecursion ( pub i32 ) ;
fn is_default < T : Default + PartialEq > ( t : & T ) -> bool {
t = = & T ::default ( )
impl ToString for MaxArchiveRecursion {
fn to_string ( & self ) -> String {
self . 0. to_string ( )
}
}
impl Default for MaxArchiveRecursion {
fn default ( ) -> Self {
MaxArchiveRecursion ( 4 )
}
}
// ugly, but serde and structopt use different methods to define defaults, so need to declare defaults twice
macro_rules! set_default {
( $name :ident , $val :expr , $type :ty ) = > {
paste ::item ! {
fn [ < def_ $name > ] ( ) -> $type {
$val
}
fn [ < def_ $name _if > ] ( e : & $type ) -> bool {
e = = & [ < def_ $name > ] ( )
}
}
} ;
#[ derive(JsonSchema, Debug, Serialize, Deserialize, Copy, Clone, PartialEq) ]
pub struct CacheMaxBlobLen ( pub usize ) ;
impl ToString for CacheMaxBlobLen {
fn to_string ( & self ) -> String {
self . 0. to_string ( )
}
}
impl Default for CacheMaxBlobLen {
fn default ( ) -> Self {
CacheMaxBlobLen ( 2000000 )
}
}
set_default ! ( cache_compression_level , 12 , u32 ) ;
set_default ! ( cache_max_blob_len , 2000000 , i64 ) ;
set_default ! ( max_archive_recursion , 4 , i32 ) ;
impl FromStr for CacheMaxBlobLen {
type Err = anyhow ::Error ;
fn from_str ( s : & str ) -> Result < Self , Self ::Err > {
let suffix = s . chars ( ) . last ( ) ;
if let Some ( suffix ) = suffix {
Ok ( CacheMaxBlobLen ( match suffix {
'k' | 'M' | 'G' = > usize ::from_str ( s . trim_end_matches ( suffix ) )
. with_context ( | | format! ( "Could not parse int" ) )
. map ( | e | {
e * match suffix {
'k' = > 1000 ,
'M' = > 1000_000 ,
'G' = > 1000_000_000 ,
_ = > panic! ( "impossible" ) ,
}
} ) ,
_ = > usize ::from_str ( s ) . with_context ( | | format! ( "Could not parse int" ) ) ,
} ? ) )
} else {
Err ( format_err ! ( "empty byte input" ) )
}
}
}
#[ derive(StructOpt, Debug, Deserialize, Serialize) ]
#[ derive(StructOpt, Debug, Deserialize, Serialize , JsonSchema, Default )]
#[ structopt(
name = "ripgrep-all" ,
rename_all = "kebab-case" ,
@ -64,7 +89,14 @@ set_default!(max_archive_recursion, 4, i32);
after_help = "-h shows a concise overview, --help shows more detail and advanced options.\n\nAll other options not shown here are passed directly to rg, especially [PATTERN] and [PATH ...]" ,
usage = "rga [RGA OPTIONS] [RG OPTIONS] PATTERN [PATH ...]"
) ]
pub struct RgaArgs {
/// # rga configuration
///
/// this is kind of a "polyglot" struct, since it serves three functions
///
/// 1. describing the command line arguments using structopt+clap
/// 2. describing the config file format (output as JSON schema via schemars)
pub struct RgaConfig {
#[ serde(default, skip_serializing_if = " is_default " ) ]
#[ structopt(long = " --rga-no-cache " ) ]
/// Disable caching of results
@ -102,50 +134,41 @@ pub struct RgaArgs {
/// "+bar,baz" means use all default adapters and also bar and baz.
pub adapters : Vec < String > ,
#[ serde(
default = "def_cache_max_blob_len" ,
skip_serializing_if = "def_cache_max_blob_len_if"
) ]
#[ serde(default, skip_serializing_if = " is_default " ) ]
#[ structopt(
default_value ,
long = "--rga-cache-max-blob-len" ,
default_value = "2000000" ,
hidden_short_help = true ,
require_equals = true ,
parse ( try_from_str = parse_readable_bytes_str )
// parse(try_from_str = parse_readable_bytes_str)
) ]
/// Max compressed size to cache
///
/// Longest byte length (after compression) to store in cache. Longer adapter outputs will not be cached and recomputed every time. Allowed suffixes: k M G
pub cache_max_blob_len : i64 ,
pub cache_max_blob_len : CacheMaxBlobLen ,
#[ serde(
default = "def_cache_compression_level" ,
skip_serializing_if = "def_cache_compression_level_if"
) ]
#[ serde(default, skip_serializing_if = " is_default " ) ]
#[ structopt(
default_value ,
long = "--rga-cache-compression-level" ,
hidden_short_help = true ,
default_value = "12" ,
require_equals = true ,
help = ""
) ]
/// ZSTD compression level to apply to adapter outputs before storing in cache db
///
/// Ranges from 1 - 22
pub cache_compression_level : u32 ,
pub cache_compression_level : CacheCompressionLevel ,
#[ serde(
default = "def_max_archive_recursion" ,
skip_serializing_if = "def_max_archive_recursion_if"
) ]
#[ serde(default, skip_serializing_if = " is_default " ) ]
#[ structopt(
default_value ,
long = "--rga-max-archive-recursion" ,
default_value = "4" ,
require_equals = true ,
help = "Maximum nestedness of archives to recurse into" ,
hidden_short_help = true
) ]
pub max_archive_recursion : i32 ,
/// Maximum nestedness of archives to recurse into
pub max_archive_recursion : MaxArchiveRecursion ,
#[ serde(skip) ]
#[ structopt(long = " --rga-fzf-path " , require_equals = true, hidden = true) ]
@ -153,11 +176,18 @@ pub struct RgaArgs {
/// kinda hacky, but if no file is found, fzf calls rga with empty string as path, which causes No such file or directory from rg. So filter those cases and return specially
pub fzf_path : Option < String > ,
// these arguments stop the process, so don't serialize them
// these arguments are basically "subcommands" that stop the process, so don't serialize them
#[ serde(skip) ]
#[ structopt(long = " --rga-list-adapters " , help = " List all known adapters " ) ]
pub list_adapters : bool ,
#[ serde(skip) ]
#[ structopt(
long = "--rga-print-config-schema" ,
help = "Print the JSON Schema of the configuration file"
) ]
pub print_config_schema : bool ,
#[ serde(skip) ]
#[ structopt(long, help = " Show help for ripgrep itself " ) ]
pub rg_help : bool ,
@ -165,15 +195,43 @@ pub struct RgaArgs {
#[ serde(skip) ]
#[ structopt(long, help = " Show version of ripgrep itself " ) ]
pub rg_version : bool ,
#[ serde(rename = " $schema " , default = " default_schema_path " ) ]
#[ structopt(skip) ]
pub _schema_key : String ,
}
fn default_schema_path ( ) -> String {
"./config.schema.json" . to_string ( )
}
static RGA_CONFIG : & str = "RGA_CONFIG" ;
pub fn parse_args < I > ( args : I ) -> Result < RgaArgs >
pub fn parse_args < I > ( args : I ) -> Result < Rga Config >
where
I : IntoIterator ,
I ::Item : Into < OsString > + Clone ,
{
let proj = project_dirs ( ) ? ;
let config_dir = proj . config_dir ( ) ;
if config_dir . join ( "config.json" ) . exists ( ) {
// todo: read config
} else {
std ::fs ::create_dir_all ( config_dir ) ? ;
let mut schemafile = File ::create ( config_dir . join ( "config.schema.json" ) ) ? ;
schemafile
. write ( serde_json ::to_string_pretty ( & schemars ::schema_for ! ( RgaConfig ) ) ? . as_bytes ( ) ) ? ;
let mut configfile = File ::create ( config_dir . join ( "config.json" ) ) ? ;
let mut v = serde_json ::to_value ( & RgaConfig ::default ( ) ) ? ;
match & mut v {
serde_json ::Value ::Object ( o ) = > {
o [ "$schema" ] = serde_json ::Value ::String ( "./config.schema.json" . to_string ( ) )
}
_ = > panic! ( "impos" ) ,
}
configfile . write ( serde_json ::to_string_pretty ( & v ) ? . as_bytes ( ) ) ? ;
}
match std ::env ::var ( RGA_CONFIG ) {
Ok ( val ) = > {
debug ! (
@ -183,7 +241,7 @@ where
Ok ( serde_json ::from_str ( & val ) ? )
}
Err ( _ ) = > {
let matches = Rga Args ::from_iter ( args ) ;
let matches = Rga Config ::from_iter ( args ) ;
let serialized_config = serde_json ::to_string ( & matches ) ? ;
std ::env ::set_var ( RGA_CONFIG , & serialized_config ) ;
debug ! ( "{}={}" , RGA_CONFIG , serialized_config ) ;
@ -194,8 +252,8 @@ where
}
/// Split arguments into the ones we care about and the ones rg cares about
pub fn split_args ( ) -> Result < ( Rga Args , Vec < OsString > ) > {
let mut app = Rga Args ::clap ( ) ;
pub fn split_args ( ) -> Result < ( Rga Config , Vec < OsString > ) > {
let mut app = Rga Config ::clap ( ) ;
app . p . create_help_and_version ( ) ;
let mut firstarg = true ;