implemented writing default configuration
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
use proc_macro2::{Ident, TokenStream};
|
use proc_macro2::{Ident, TokenStream};
|
||||||
use syn::{Attribute, Field, Meta, MetaList, Path, punctuated::Punctuated, token::Comma};
|
use syn::{punctuated::Punctuated, token::Comma, Attribute, Expr, ExprLit, Field, Meta, MetaList, MetaNameValue, Path};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
|
||||||
pub fn gen_parse_from_string(config_name: &Ident, output_name: &Ident, assignments: &TokenStream) -> TokenStream {
|
pub fn gen_parse_from_string(config_name: &Ident, output_name: &Ident, assignments: &TokenStream) -> TokenStream {
|
||||||
@@ -133,8 +133,76 @@ pub fn gen_default(fields: &Punctuated<Field, Comma>) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_config_assignments(fields: &Punctuated<Field, Comma>, config_map_name: &syn::Ident, output_name: &syn::Ident) -> TokenStream {
|
pub fn gen_to_string(fields: &Punctuated<Field, Comma>) -> TokenStream {
|
||||||
let mut assignments : TokenStream = TokenStream::new();
|
let mut statements: TokenStream = TokenStream::new();
|
||||||
|
let mut nested_statements: TokenStream = TokenStream::new();
|
||||||
|
let mut comment: TokenStream = TokenStream::new();
|
||||||
|
'fields: for field in fields.iter() {
|
||||||
|
let attr = &field.attrs;
|
||||||
|
let name = match &field.ident {
|
||||||
|
Some(value) => value,
|
||||||
|
None => continue 'fields,
|
||||||
|
};
|
||||||
|
let name_string = name.to_string();
|
||||||
|
// TODO: continue here
|
||||||
|
for attribute in attr {
|
||||||
|
if let Attribute{ meta: Meta::Path( Path{segments, ..} ), .. } = attribute {
|
||||||
|
// parse nested configs or skip nonconfig elements
|
||||||
|
match segments.first() {
|
||||||
|
Some(attr_name) => {
|
||||||
|
if attr_name.ident == "nested_config" {
|
||||||
|
statements.extend(quote! {
|
||||||
|
string.push_str(&format!("\n[{}]\n", #name_string));
|
||||||
|
string.push_str(&self.#name.to_string_nested(&format!("{}", #name_string)));
|
||||||
|
});
|
||||||
|
nested_statements.extend(quote! {
|
||||||
|
string.push_str(&format!("\n[{}.{}]\n", parents, #name_string));
|
||||||
|
string.push_str(&self.#name.to_string_nested(&format!("{}.{}", parents, #name_string)));
|
||||||
|
});
|
||||||
|
continue 'fields;
|
||||||
|
} else if attr_name.ident == "no_config" {
|
||||||
|
continue 'fields;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
} else if let Attribute{ meta: Meta::NameValue( MetaNameValue{path: Path{ segments, .. }, value: Expr::Lit(ExprLit{lit, ..}), ..} ), .. } = attribute {
|
||||||
|
// write comments to string
|
||||||
|
let attr_type = segments.first().unwrap();
|
||||||
|
if attr_type.ident == "doc" {
|
||||||
|
comment = quote!{
|
||||||
|
format!(" #{}", #lit)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if comment.is_empty() {
|
||||||
|
comment.extend(quote! {""});
|
||||||
|
}
|
||||||
|
let code = quote!{
|
||||||
|
string.push_str(&format!("{} = {}{}\n", #name_string, self.#name.to_string(), #comment));
|
||||||
|
};
|
||||||
|
statements.extend(code.clone());
|
||||||
|
nested_statements.extend(code);
|
||||||
|
}
|
||||||
|
quote! {
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
let mut string = String::new();
|
||||||
|
#statements
|
||||||
|
string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// macro function - do not call
|
||||||
|
pub fn to_string_nested(&self, parents: &String) -> String {
|
||||||
|
let mut string = String::new();
|
||||||
|
#nested_statements
|
||||||
|
string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gen_config_assignments(fields: &Punctuated<Field, Comma>, config_map_name: &Ident, output_name: &Ident) -> TokenStream {
|
||||||
|
let mut assignments: TokenStream = TokenStream::new();
|
||||||
'fields: for field in fields.iter() {
|
'fields: for field in fields.iter() {
|
||||||
let attr = &field.attrs;
|
let attr = &field.attrs;
|
||||||
let name = match &field.ident {
|
let name = match &field.ident {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|||||||
let func_parse_map: TokenStream = gen_parse_from_map(&config_name, &output_name, &assignments);
|
let func_parse_map: TokenStream = gen_parse_from_map(&config_name, &output_name, &assignments);
|
||||||
let func_to_ansi_sequences: TokenStream = gen_to_ansi_sequences(fields);
|
let func_to_ansi_sequences: TokenStream = gen_to_ansi_sequences(fields);
|
||||||
let func_default: TokenStream = gen_default(fields);
|
let func_default: TokenStream = gen_default(fields);
|
||||||
|
let func_to_string: TokenStream = gen_to_string(fields);
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
impl #name {
|
impl #name {
|
||||||
@@ -42,8 +43,7 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|||||||
#func_parse_map
|
#func_parse_map
|
||||||
#func_to_ansi_sequences
|
#func_to_ansi_sequences
|
||||||
#func_default
|
#func_default
|
||||||
|
#func_to_string
|
||||||
// TODO: implement function to parse style settings
|
|
||||||
}
|
}
|
||||||
}.into()
|
}.into()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ function book {
|
|||||||
__call_navigate "bookmark $@"
|
__call_navigate "bookmark $@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function nav_config {
|
||||||
|
__call_navigate "configuration $@"
|
||||||
|
}
|
||||||
|
|
||||||
# completion function for `book`
|
# completion function for `book`
|
||||||
function _book {
|
function _book {
|
||||||
CURRENT_WORD=${COMP_WORDS[COMP_CWORD]}
|
CURRENT_WORD=${COMP_WORDS[COMP_CWORD]}
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ pub enum Action {
|
|||||||
|
|
||||||
/// navigate to bookmark and add current path to the stack
|
/// navigate to bookmark and add current path to the stack
|
||||||
bookmark(BookmarkArgs),
|
bookmark(BookmarkArgs),
|
||||||
|
|
||||||
|
/// display current configuartion (mostly for debugging)
|
||||||
|
configuration(ConfigArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Args)]
|
#[derive(Debug, Clone, Args)]
|
||||||
@@ -117,6 +120,13 @@ pub struct BookmarkSubArgs {
|
|||||||
pub path: Option<PathBuf>,
|
pub path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Args)]
|
||||||
|
pub struct ConfigArgs {
|
||||||
|
/// convert styles to ansi escape sequences
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub convert: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
/// empty struct for subcommands with no arguments
|
/// empty struct for subcommands with no arguments
|
||||||
#[derive(Debug, Clone, Args)]
|
#[derive(Debug, Clone, Args)]
|
||||||
pub struct EmptyArgs {}
|
pub struct EmptyArgs {}
|
||||||
|
|||||||
+68
-18
@@ -20,68 +20,109 @@ pub struct Config {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, ConfigParser)]
|
#[derive(Debug, Clone, Default, ConfigParser)]
|
||||||
pub struct GeneralSettings {
|
pub struct GeneralSettings {
|
||||||
|
/// (bool) show stack when pushing a path to the stack
|
||||||
#[default_value(false)]
|
#[default_value(false)]
|
||||||
pub show_stack_on_push: bool,
|
pub show_stack_on_push: bool,
|
||||||
|
|
||||||
|
/// (bool) show stack when popping a stack entry
|
||||||
#[default_value(false)]
|
#[default_value(false)]
|
||||||
pub show_stack_on_pop: bool,
|
pub show_stack_on_pop: bool,
|
||||||
|
|
||||||
|
/// (bool) show book marks when adding, removing or changing to a bookmark
|
||||||
#[default_value(false)]
|
#[default_value(false)]
|
||||||
pub show_books_on_bookmark: bool,
|
pub show_books_on_bookmark: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, ConfigParser)]
|
#[derive(Debug, Clone, Default, ConfigParser)]
|
||||||
pub struct FormatSettings {
|
pub struct FormatSettings {
|
||||||
|
/// (bool) add padding before the separator if true, after if false
|
||||||
#[default_value(true)]
|
#[default_value(true)]
|
||||||
pub align_separators: bool,
|
pub align_separators: bool,
|
||||||
#[default_value(" - ")]
|
|
||||||
pub stack_separator: String,
|
/// (bool) replace home directory path with '~' when displaying the stack or bookmarks
|
||||||
#[default_value(false)]
|
#[default_value(false)]
|
||||||
pub show_home_as_tilde: bool,
|
pub show_home_as_tilde: bool,
|
||||||
#[default_value(" - ")]
|
|
||||||
|
/// (string) separator between stack numbers and paths
|
||||||
|
#[default_value("' - '")]
|
||||||
|
pub stack_separator: String,
|
||||||
|
|
||||||
|
/// (string) separator between bookmark names and paths
|
||||||
|
#[default_value("' - '")]
|
||||||
pub bookmarks_separator: String,
|
pub bookmarks_separator: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, ConfigParser)]
|
#[derive(Debug, Clone, Default, ConfigParser)]
|
||||||
pub struct StyleSettings {
|
pub struct StyleSettings {
|
||||||
|
/// (string) style applied to warnings
|
||||||
#[style_config]
|
#[style_config]
|
||||||
#[default_value("yellow, italic")]
|
#[default_value("'yellow, italic'")]
|
||||||
pub warning_style: String,
|
pub warning_style: String,
|
||||||
|
|
||||||
|
/// (string) style applied to errors
|
||||||
#[style_config]
|
#[style_config]
|
||||||
#[default_value("red, bold")]
|
#[default_value("'red, bold'")]
|
||||||
pub error_style: String,
|
pub error_style: String,
|
||||||
|
|
||||||
|
/// (string) style applied to numbers when displaying the stack
|
||||||
#[style_config]
|
#[style_config]
|
||||||
#[default_value("default")]
|
#[default_value("'default'")]
|
||||||
pub stack_number_style: String,
|
pub stack_number_style: String,
|
||||||
|
|
||||||
|
/// (string) style applied to separators when displaying the stack
|
||||||
#[style_config]
|
#[style_config]
|
||||||
#[default_value("cyan")]
|
#[default_value("'cyan'")]
|
||||||
pub stack_separator_style: String,
|
pub stack_separator_style: String,
|
||||||
|
|
||||||
|
/// (string) style applied to paths when displaying the stack
|
||||||
#[style_config]
|
#[style_config]
|
||||||
#[default_value("default")]
|
#[default_value("'default'")]
|
||||||
pub stack_path_style: String,
|
pub stack_path_style: String,
|
||||||
|
|
||||||
|
/// (string) style applied to punctuation (i.e. '/') when displaying the stack
|
||||||
#[style_config]
|
#[style_config]
|
||||||
#[default_value("magenta")]
|
#[default_value("'magenta'")]
|
||||||
pub stack_punct_style: String,
|
pub stack_punct_style: String,
|
||||||
|
|
||||||
|
/// (string) style applied to bookmark names when displaying the bookmarks
|
||||||
#[style_config]
|
#[style_config]
|
||||||
#[default_value("default")]
|
#[default_value("'default'")]
|
||||||
pub bookmarks_name_style: String,
|
pub bookmarks_name_style: String,
|
||||||
|
|
||||||
|
/// (string) style applied to separators when displaying the bookmarks
|
||||||
#[style_config]
|
#[style_config]
|
||||||
#[default_value("cyan")]
|
#[default_value("'cyan'")]
|
||||||
pub bookmarks_seperator_style: String,
|
pub bookmarks_seperator_style: String,
|
||||||
|
|
||||||
|
/// (string) style applied to paths when displaying the bookmarks
|
||||||
#[style_config]
|
#[style_config]
|
||||||
#[default_value("default")]
|
#[default_value("'default'")]
|
||||||
pub bookmarks_path_style: String,
|
pub bookmarks_path_style: String,
|
||||||
|
|
||||||
|
/// (string) style applied to punctuation (i.e. '/') when displaying the bookmarks
|
||||||
#[style_config]
|
#[style_config]
|
||||||
#[default_value("magenta")]
|
#[default_value("'magenta'")]
|
||||||
pub bookmarks_punct_style: String,
|
pub bookmarks_punct_style: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
const CONFIG_DIRECTORY_NAME: &str = "navigate";
|
||||||
const CONFIG_FILE_NAME: &str = "navigate.toml";
|
const CONFIG_FILE_NAME: &str = "navigate.toml";
|
||||||
|
const DEFAULT_CONFIG_NAME: &str = "default.toml";
|
||||||
|
const DEFAULT_FILE_HEADER: &str = "
|
||||||
|
# default configuration file for `navigate`
|
||||||
|
#
|
||||||
|
# value type should be in the comment
|
||||||
|
# string values have to be quoted, both single and double quotes work
|
||||||
|
# integer and boolean values do not need quotes
|
||||||
|
# boolean values are either `true` or `false`
|
||||||
|
";
|
||||||
|
|
||||||
/// generates and populates a new instance of Config
|
/// generates and populates a new instance of Config
|
||||||
pub fn new(styles_as_ansi_sequences: bool) -> Result<Self> {
|
pub fn new(styles_as_ansi_sequences: bool) -> Result<Self> {
|
||||||
let mut config: Config = Self::default();
|
let mut config: Config = Self::default();
|
||||||
// get configuration directory
|
// get configuration directory
|
||||||
let mut conf_file = match config_dir() {
|
let mut config_file = match config_dir() {
|
||||||
Some(value) => value,
|
Some(value) => value,
|
||||||
None => {
|
None => {
|
||||||
return Err(Error::other(
|
return Err(Error::other(
|
||||||
@@ -89,12 +130,21 @@ impl Config {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// expand path to configuration file
|
// expand path to configuration file and default configuration file
|
||||||
conf_file.push(format!("navigate/{}", Self::CONFIG_FILE_NAME));
|
let mut default_file = config_file.clone();
|
||||||
|
default_file.push(format!("{}/{}", Self::CONFIG_DIRECTORY_NAME, Self::DEFAULT_CONFIG_NAME));
|
||||||
|
config_file.push(format!("{}/{}", Self::CONFIG_DIRECTORY_NAME, Self::CONFIG_FILE_NAME));
|
||||||
|
|
||||||
|
// write default configuration file if it does not exist
|
||||||
|
if !default_file.is_file() {
|
||||||
|
let mut default_string = Self::DEFAULT_FILE_HEADER.to_string();
|
||||||
|
default_string.push_str(&config.to_string());
|
||||||
|
_ = fs::write(default_file, default_string);
|
||||||
|
}
|
||||||
|
|
||||||
// parse configuration file and populate config struct
|
// parse configuration file and populate config struct
|
||||||
if conf_file.is_file() {
|
if config_file.is_file() {
|
||||||
let config_str = match fs::read_to_string(&conf_file) {
|
let config_str = match fs::read_to_string(&config_file) {
|
||||||
Ok(value) => value,
|
Ok(value) => value,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
|
|||||||
+22
-8
@@ -28,14 +28,24 @@ fn main() -> Result<()> {
|
|||||||
Err(error) => {
|
Err(error) => {
|
||||||
// config object is not ready at this point so the style
|
// config object is not ready at this point so the style
|
||||||
// has to be created by hand
|
// has to be created by hand
|
||||||
print!("echo '{}{}{}' && false", generate_style_sequence(None, Some(COLORS.fg.red), None), error, RESET_SEQ);
|
print!("echo -e '{}{}{}' && false", generate_style_sequence(None, Some(COLORS.fg.red), None), error, RESET_SEQ);
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let args = match Arguments::try_parse() {
|
let args = match Arguments::try_parse() {
|
||||||
Ok(a) => a,
|
Ok(a) => a,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
output.push_error(&error.to_string());
|
match error.kind() {
|
||||||
|
clap::error::ErrorKind::DisplayHelp | clap::error::ErrorKind::DisplayVersion => {
|
||||||
|
output.push_info(&error.to_string());
|
||||||
|
},
|
||||||
|
clap::error::ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand => {
|
||||||
|
output.push_warning(&error.to_string());
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
output.push_error(&error.to_string());
|
||||||
|
},
|
||||||
|
}
|
||||||
output.print_output(&config);
|
output.print_output(&config);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -60,6 +70,7 @@ fn main() -> Result<()> {
|
|||||||
Action::pop(pop_args) => handle_pop(&pop_args, &config, &mut stack, &mut output),
|
Action::pop(pop_args) => handle_pop(&pop_args, &config, &mut stack, &mut output),
|
||||||
Action::stack(stack_args) => handle_stack(&stack_args, &config, &mut stack, &mut output),
|
Action::stack(stack_args) => handle_stack(&stack_args, &config, &mut stack, &mut output),
|
||||||
Action::bookmark(bookmark_args) => handle_bookmark(&bookmark_args, &config, &mut bookmarks, &mut stack, &mut output),
|
Action::bookmark(bookmark_args) => handle_bookmark(&bookmark_args, &config, &mut bookmarks, &mut stack, &mut output),
|
||||||
|
Action::configuration(config_args) => handle_config(&config_args, &mut output),
|
||||||
};
|
};
|
||||||
|
|
||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
@@ -156,12 +167,15 @@ fn handle_bookmark(args: &BookmarkArgs, config: &Config, bookmarks: &mut Bookmar
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn handle_config(args: &ConfigArgs, config: &Config) -> Result<()> {
|
fn handle_config(args: &ConfigArgs, output: &mut Output) -> Result<()> {
|
||||||
// match args {
|
let convert: bool = match args.convert {
|
||||||
// ConfigAction::show => println!("echo '{}'", config.to_formatted_string()),
|
Some(value) => value,
|
||||||
// }
|
None => false,
|
||||||
// Ok(())
|
};
|
||||||
// }
|
let config = Config::new(convert);
|
||||||
|
output.push_info(&format!("config = {:#?}", config));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn list_bookmarks(config: &Config, bookmarks: &mut Bookmarks, output: &mut Output) -> Result<()> {
|
fn list_bookmarks(config: &Config, bookmarks: &mut Bookmarks, output: &mut Output) -> Result<()> {
|
||||||
output.push_info(&bookmarks.to_formatted_string(config)?);
|
output.push_info(&bookmarks.to_formatted_string(config)?);
|
||||||
|
|||||||
Reference in New Issue
Block a user