pub mod context_accounts_metadata;
pub mod entrypoint_metadata;
pub mod enums_source_code_metadata;
pub mod function_dependencies_metadata;
pub mod functions_source_code_metadata;
pub mod miro_metadata;
pub mod program_accounts_metadata;
pub mod structs_source_code_metadata;
pub mod trait_metadata;
pub mod traits_source_code_metadata;
use colored::Colorize;
use std::error::Error;
use std::fmt::{Debug, Display};
use std::sync::Mutex;
use std::{env, fmt};
static METADATA_FILE_LOCK: Mutex<()> = Mutex::new(());
use crate::batbelt::path::BatFile;
use inflector::Inflector;
use crate::batbelt::bat_dialoguer::BatDialoguer;
use crate::batbelt::metadata::context_accounts_metadata::ContextAccountsMetadata;
use crate::batbelt::metadata::entrypoint_metadata::EntrypointMetadata;
use crate::batbelt::metadata::function_dependencies_metadata::FunctionDependenciesMetadata;
use crate::batbelt::metadata::functions_source_code_metadata::{
FunctionMetadataType, FunctionSourceCodeMetadata,
};
use crate::batbelt::metadata::miro_metadata::MiroCodeOverhaulMetadata;
use crate::batbelt::metadata::structs_source_code_metadata::{
StructMetadataType, StructSourceCodeMetadata,
};
use crate::batbelt::metadata::trait_metadata::TraitMetadata;
use crate::batbelt::metadata::traits_source_code_metadata::{
TraitMetadataType, TraitSourceCodeMetadata,
};
use crate::batbelt::parser::parse_formatted_path;
use crate::batbelt::parser::source_code_parser::SourceCodeParser;
use crate::batbelt::BatEnumerator;
use crate::Suggestion;
use error_stack::{FutureExt, IntoReport, Report, Result, ResultExt};
use rand::distributions::Alphanumeric;
use rand::Rng;
use serde::{Deserialize, Serialize};
use crate::batbelt::git::git_commit::GitCommit;
use crate::batbelt::metadata::enums_source_code_metadata::{
EnumMetadataType, EnumSourceCodeMetadata,
};
use crate::batbelt::metadata::program_accounts_metadata::ProgramAccountMetadata;
use crate::config::{BatAuditorConfig, BatConfig};
use serde_json::{json, Value};
use strum::IntoEnumIterator;
use walkdir::DirEntry;
#[derive(Debug)]
pub struct MetadataError;
impl fmt::Display for MetadataError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Metadata error")
}
}
impl Error for MetadataError {}
pub type MetadataResult<T> = Result<T, MetadataError>;
pub type MetadataId = String;
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct BatMetadata {
#[serde(default)]
pub project_name: String,
pub initialized: bool,
pub source_code: SourceCodeMetadata,
pub entry_points: Vec<EntrypointMetadata>,
pub function_dependencies: Vec<FunctionDependenciesMetadata>,
pub traits: Vec<TraitMetadata>,
pub context_accounts: Vec<ContextAccountsMetadata>,
pub miro: MiroMetadata,
}
impl BatMetadata {
pub fn new_empty() -> Self {
let bat_config = BatConfig::get_config().unwrap();
Self {
project_name: bat_config.project_name,
initialized: false,
source_code: Default::default(),
entry_points: vec![],
function_dependencies: vec![],
traits: vec![],
context_accounts: vec![],
miro: Default::default(),
}
}
pub fn create_metadata_id() -> String {
let s: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(30)
.map(char::from)
.collect();
s
}
pub fn read_metadata() -> MetadataResult<Self> {
let _guard = METADATA_FILE_LOCK.lock().unwrap();
Self::read_metadata_unlocked()
}
fn read_metadata_unlocked() -> MetadataResult<Self> {
if BatMetadataEnvVariables::assert_use_external_metadata() {
return Self::read_external_metadata();
}
let metadata_json_bat_file = BatFile::BatMetadataFile;
let bat_metadata_value: Value = serde_json::from_str(
&metadata_json_bat_file
.read_content(true)
.change_context(MetadataError)?,
)
.into_report()
.change_context(MetadataError)?;
let mut bat_metadata: BatMetadata = serde_json::from_value(bat_metadata_value)
.into_report()
.change_context(MetadataError)?;
if bat_metadata.project_name.is_empty() {
bat_metadata.project_name = BatConfig::get_config()
.change_context(MetadataError)?
.project_name;
bat_metadata.save_metadata_unlocked()?;
GitCommit::UpdateMetadataJson {
bat_metadata_commit: BatMetadataCommit::UpdateMetadataVersion,
}
.create_commit(true)
.change_context(MetadataError)?;
}
Ok(bat_metadata)
}
fn read_external_metadata() -> MetadataResult<Self> {
let external_bat_metadata_selected = match BatMetadataEnvVariables::BatMetadataFileSelected
.read_value()
{
Ok(external_bat_path) => external_bat_path,
Err(_) => {
let bat_auditor_config =
BatAuditorConfig::get_config().change_context(MetadataError)?;
let prompt_text = format!("Select the {} file to use:", "BatMetadata.json".green());
let selection = BatDialoguer::select(
prompt_text,
bat_auditor_config.external_bat_metadata.clone(),
None,
)
.change_context(MetadataError)?;
let external_bat_metadata_selected =
bat_auditor_config.external_bat_metadata[selection].clone();
BatMetadataEnvVariables::UseExternalMetadata
.set_value(&external_bat_metadata_selected);
external_bat_metadata_selected
}
};
let metadata_json_bat_file = BatFile::Generic {
file_path: external_bat_metadata_selected.clone(),
};
let bat_metadata_value: Value = serde_json::from_str(
&metadata_json_bat_file
.read_content(true)
.change_context(MetadataError)?,
)
.into_report()
.change_context(MetadataError)?;
let bat_metadata: BatMetadata = serde_json::from_value(bat_metadata_value)
.into_report()
.attach_printable(format!(
"{} file at path {} is incompatible with this bat-cli version",
"BatMetadata.json".bright_green(),
external_bat_metadata_selected.clone()
))
.attach(Suggestion(format!(
"run {} at {} to update the BatMetadata.json version",
"bat-cli sonar".bright_green(),
external_bat_metadata_selected.clone().bright_yellow()
)))
.change_context(MetadataError)?;
Ok(bat_metadata)
}
pub fn save_metadata(&self) -> MetadataResult<()> {
let _guard = METADATA_FILE_LOCK.lock().unwrap();
self.save_metadata_unlocked()
}
fn save_metadata_unlocked(&self) -> MetadataResult<()> {
let bat_config = BatConfig::get_config().change_context(MetadataError)?;
if self.project_name != bat_config.project_name {
return Err(Report::new(MetadataError).attach_printable(format!(
"Error saving {}, expected project_name {:#?}, got {:#?}",
"BatMetadata.json".bright_green(),
bat_config.project_name,
self.project_name
)));
}
let metadata_json_bat_file = BatFile::BatMetadataFile;
let metadata_json = json!(&self);
let metadata_json_pretty = serde_json::to_string_pretty(&metadata_json)
.into_report()
.change_context(MetadataError)?;
metadata_json_bat_file
.write_content(false, &metadata_json_pretty)
.change_context(MetadataError)?;
Ok(())
}
pub fn update_metadata<F>(f: F) -> MetadataResult<()>
where
F: FnOnce(&mut BatMetadata),
{
let _guard = METADATA_FILE_LOCK.lock().unwrap();
let mut bat_metadata = Self::read_metadata_unlocked()?;
f(&mut bat_metadata);
bat_metadata.save_metadata_unlocked()?;
Ok(())
}
pub fn get_entrypoint_metadata_by_name(
&self,
entry_point_name: String,
) -> MetadataResult<EntrypointMetadata> {
if self.entry_points.is_empty() {
return Err(MetadataErrorReports::EntryPointsMetadataNotInitialized.get_error_report());
}
match self
.entry_points
.clone()
.into_iter()
.find(|ep| ep.name == entry_point_name)
{
None => Err(
MetadataErrorReports::EntryPointNameNotFound { entry_point_name }
.get_error_report(),
),
Some(ep) => Ok(ep),
}
}
pub fn get_functions_dependencies_metadata_by_function_metadata_id(
&self,
function_metadata_id: String,
) -> MetadataResult<FunctionDependenciesMetadata> {
if self.function_dependencies.is_empty() {
return Err(
MetadataErrorReports::FunctionDependenciesMetadataNotInitialized.get_error_report(),
);
}
match self
.function_dependencies
.clone()
.into_iter()
.find(|ep| ep.function_metadata_id == function_metadata_id)
{
None => Err(MetadataErrorReports::FunctionDependenciesNotFound {
function_metadata_id,
}
.get_error_report()),
Some(metadata) => Ok(metadata),
}
}
pub fn get_trait_metadata_by_trait_source_code_metadata_id(
&self,
trait_source_code_metadata_id: String,
) -> MetadataResult<TraitMetadata> {
if self.function_dependencies.is_empty() {
return Err(MetadataErrorReports::TraitsMetadataNotInitialized.get_error_report());
}
match self
.traits
.clone()
.into_iter()
.find(|meta| meta.trait_source_code_metadata_id == trait_source_code_metadata_id)
{
None => Err(MetadataErrorReports::TraitNotFound {
trait_source_code_metadata_id,
}
.get_error_report()),
Some(metadata) => Ok(metadata),
}
}
pub fn get_context_accounts_metadata_by_struct_source_code_metadata_id(
&self,
struct_source_code_metadata_id: String,
) -> MetadataResult<ContextAccountsMetadata> {
if self.context_accounts.is_empty() {
return Err(
MetadataErrorReports::ContextAccountsMetadataNotInitialized.get_error_report()
);
}
match self
.context_accounts
.clone()
.into_iter()
.find(|meta| meta.struct_source_code_metadata_id == struct_source_code_metadata_id)
{
None => Err(MetadataErrorReports::ContextAccountsNotFound {
struct_source_code_metadata_id,
}
.get_error_report()),
Some(metadata) => Ok(metadata),
}
}
pub fn check_metadata_is_initialized(&self) -> Result<(), MetadataError> {
if !self.initialized {
return Err(MetadataErrorReports::MetadataNotInitialized.get_error_report());
}
Ok(())
}
}
#[derive(
Debug,
PartialEq,
Clone,
Copy,
strum_macros::Display,
strum_macros::EnumIter,
Serialize,
Deserialize,
)]
pub enum BatMetadataEnvVariables {
UseExternalMetadata,
BatMetadataFileSelected,
}
impl BatEnumerator for BatMetadataEnvVariables {}
impl BatMetadataEnvVariables {
pub fn set_use_external_metadata_to_true() -> MetadataResult<()> {
let bat_auditor_config = BatAuditorConfig::get_config().change_context(MetadataError)?;
if bat_auditor_config.external_bat_metadata.is_empty() {
return Err(Report::new(MetadataError)
.attach_printable("external_bat_metadata vector is empty on BatAuditor.toml"))
.attach(Suggestion(format!(
"run {} to add external BatMetadata.json files",
"bat-cli reload".bright_green()
)));
}
let use_external = Self::UseExternalMetadata;
let new_value = "true";
use_external.set_value(new_value);
Ok(())
}
pub fn set_use_external_metadata_to_false() {
let use_external = Self::UseExternalMetadata;
let new_value = "false";
use_external.set_value(new_value);
}
pub fn assert_use_external_metadata() -> bool {
let use_external = Self::UseExternalMetadata;
match use_external.read_value() {
Ok(value) => value == "true".to_string(),
Err(_) => false,
}
}
pub fn get_variable_key(&self) -> String {
self.to_string().to_screaming_snake_case()
}
pub fn set_value(&self, new_value: &str) {
let key = self.get_variable_key();
env::set_var(key, new_value);
}
pub fn read_value(&self) -> MetadataResult<String> {
let key = self.get_variable_key();
env::var(key).into_report().change_context(MetadataError)
}
pub fn clean_value(&self) {
let key = self.get_variable_key();
env::remove_var(key)
}
}
#[derive(Serialize, Deserialize, Clone)]
pub enum BatMetadataCommit {
RunSonarMetadataCommit,
MiroMetadataCommit,
UpdateMetadataVersion,
}
impl BatMetadataCommit {
pub fn get_commit_message(&self) -> String {
match self {
BatMetadataCommit::RunSonarMetadataCommit => {
"metadata: bat-cli sonar executed".to_string()
}
BatMetadataCommit::MiroMetadataCommit => "metadata: miro metadata updated".to_string(),
BatMetadataCommit::UpdateMetadataVersion => {
"metadata: BatMetadata.json updated to last version".to_string()
}
}
}
}
enum MetadataErrorReports {
MetadataNotInitialized,
MetadataIdNotFound {
metadata_id: MetadataId,
},
EntryPointsMetadataNotInitialized,
EntryPointNameNotFound {
entry_point_name: String,
},
FunctionDependenciesMetadataNotInitialized,
FunctionDependenciesNotFound {
function_metadata_id: MetadataId,
},
TraitsMetadataNotInitialized,
TraitNotFound {
trait_source_code_metadata_id: MetadataId,
},
ContextAccountsMetadataNotInitialized,
ContextAccountsNotFound {
struct_source_code_metadata_id: MetadataId,
},
MiroCodeOverhaulMetadataNotInitialized,
MiroCodeOverhaulMetadataNotFound {
entry_point_name: String,
},
}
impl MetadataErrorReports {
pub fn get_error_report(&self) -> Report<MetadataError> {
let initialize_suggestion = Suggestion(format!(
"Initialize the BatMetadata by running {}",
"bat-cli sonar".green()
));
let message = match self {
MetadataErrorReports::MetadataNotInitialized => {
"Metadata is not initialized".to_string()
}
MetadataErrorReports::MetadataIdNotFound { metadata_id } => {
format!("Metadata not found for {}", metadata_id.red())
}
MetadataErrorReports::EntryPointsMetadataNotInitialized => {
"Entry point metadata has not been initialized".to_string()
}
MetadataErrorReports::EntryPointNameNotFound { entry_point_name } => {
format!(
"Entry point metadata not found for {}",
entry_point_name.red()
)
}
MetadataErrorReports::FunctionDependenciesMetadataNotInitialized => {
"Function dependencies metadata has not been initialized".to_string()
}
MetadataErrorReports::FunctionDependenciesNotFound {
function_metadata_id,
} => {
format!(
"Entry point metadata not found for {} id",
function_metadata_id.red()
)
}
MetadataErrorReports::TraitsMetadataNotInitialized => {
"Traits metadata has not been initialized".to_string()
}
MetadataErrorReports::TraitNotFound {
trait_source_code_metadata_id: trait_metadata_id,
} => {
format!(
"Trait metadata not found for {} id",
trait_metadata_id.red()
)
}
MetadataErrorReports::ContextAccountsMetadataNotInitialized => {
"Context accounts metadata has not been initialized".to_string()
}
MetadataErrorReports::ContextAccountsNotFound {
struct_source_code_metadata_id,
} => {
format!(
"Context accounts metadata not found for {} id",
struct_source_code_metadata_id.red()
)
}
MetadataErrorReports::MiroCodeOverhaulMetadataNotInitialized => {
"Miro code-overhaul's metadata has not been initialized".to_string()
}
MetadataErrorReports::MiroCodeOverhaulMetadataNotFound { entry_point_name } => {
format!(
"Miro code-overhaul's metadata not found for {:#?} entry point",
entry_point_name.red()
)
}
};
Report::new(MetadataError)
.attach_printable(message)
.attach(initialize_suggestion)
}
}
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
pub struct MiroMetadata {
pub code_overhaul: Vec<MiroCodeOverhaulMetadata>,
}
impl MiroMetadata {
pub fn new(code_overhaul: Vec<MiroCodeOverhaulMetadata>) -> Self {
Self { code_overhaul }
}
pub fn get_co_metadata_by_entrypoint_name(
entry_point_name: String,
) -> MetadataResult<MiroCodeOverhaulMetadata> {
let bat_metadata = BatMetadata::read_metadata()?;
if bat_metadata.miro.code_overhaul.is_empty() {
return Err(
MetadataErrorReports::MiroCodeOverhaulMetadataNotInitialized.get_error_report()
);
}
match bat_metadata
.miro
.code_overhaul
.into_iter()
.find(|meta| meta.entry_point_name == entry_point_name)
{
None => {
Err(
MetadataErrorReports::MiroCodeOverhaulMetadataNotFound { entry_point_name }
.get_error_report(),
)
}
Some(co_meta) => Ok(co_meta),
}
}
}
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
pub struct SourceCodeMetadata {
pub functions_source_code: Vec<FunctionSourceCodeMetadata>,
pub structs_source_code: Vec<StructSourceCodeMetadata>,
pub traits_source_code: Vec<TraitSourceCodeMetadata>,
pub enums_source_code: Vec<EnumSourceCodeMetadata>,
}
impl SourceCodeMetadata {
pub fn get_function_by_id(
&self,
metadata_id: MetadataId,
) -> MetadataResult<FunctionSourceCodeMetadata> {
let result = self
.functions_source_code
.clone()
.into_iter()
.find(|meta| meta.metadata_id == metadata_id);
match result {
Some(f_metadata) => Ok(f_metadata),
None => {
Err(MetadataErrorReports::MetadataIdNotFound { metadata_id }.get_error_report())
}
}
}
pub fn get_struct_by_id(
&self,
metadata_id: MetadataId,
) -> MetadataResult<StructSourceCodeMetadata> {
let result = self
.structs_source_code
.clone()
.into_iter()
.find(|meta| meta.metadata_id == metadata_id);
match result {
Some(metadata) => Ok(metadata),
None => {
Err(MetadataErrorReports::MetadataIdNotFound { metadata_id }.get_error_report())
}
}
}
pub fn get_trait_by_id(
&self,
metadata_id: MetadataId,
) -> MetadataResult<TraitSourceCodeMetadata> {
let result = self
.traits_source_code
.clone()
.into_iter()
.find(|meta| meta.metadata_id == metadata_id);
match result {
Some(metadata) => Ok(metadata),
None => {
Err(MetadataErrorReports::MetadataIdNotFound { metadata_id }.get_error_report())
}
}
}
pub fn update_functions(&self, new_vec: Vec<FunctionSourceCodeMetadata>) -> MetadataResult<()> {
let mut bat_metadata = BatMetadata::read_metadata()?;
let mut metadata_vec = new_vec;
metadata_vec.sort_by_key(|metadata_item| metadata_item.name());
bat_metadata.source_code.functions_source_code = metadata_vec;
bat_metadata.save_metadata()?;
Ok(())
}
pub fn update_structs(&self, new_vec: Vec<StructSourceCodeMetadata>) -> MetadataResult<()> {
let mut bat_metadata = BatMetadata::read_metadata()?;
let mut metadata_vec = new_vec;
metadata_vec.sort_by_key(|metadata_item| metadata_item.name());
bat_metadata.source_code.structs_source_code = metadata_vec;
bat_metadata.save_metadata()?;
Ok(())
}
pub fn update_traits(&self, new_vec: Vec<TraitSourceCodeMetadata>) -> MetadataResult<()> {
let mut bat_metadata = BatMetadata::read_metadata()?;
let mut metadata_vec = new_vec;
metadata_vec.sort_by_key(|metadata_item| metadata_item.name());
bat_metadata.source_code.traits_source_code = metadata_vec;
bat_metadata.save_metadata()?;
Ok(())
}
pub fn update_enums(&self, new_vec: Vec<EnumSourceCodeMetadata>) -> MetadataResult<()> {
let mut bat_metadata = BatMetadata::read_metadata()?;
let mut metadata_vec = new_vec;
metadata_vec.sort_by_key(|metadata_item| metadata_item.name());
bat_metadata.source_code.enums_source_code = metadata_vec;
bat_metadata.save_metadata()?;
Ok(())
}
pub fn get_filtered_structs(
struct_name: Option<String>,
struct_type: Option<StructMetadataType>,
) -> MetadataResult<Vec<StructSourceCodeMetadata>> {
Ok(BatMetadata::read_metadata()?
.source_code
.structs_source_code
.into_iter()
.filter(|struct_metadata| {
if struct_name.is_some() && struct_name.clone().unwrap() != struct_metadata.name {
return false;
};
if struct_type.is_some() && struct_type.unwrap() != struct_metadata.struct_type {
return false;
};
true
})
.collect::<Vec<_>>())
}
pub fn find_struct(
struct_name: String,
struct_type: StructMetadataType,
) -> MetadataResult<StructSourceCodeMetadata> {
match BatMetadata::read_metadata()?
.source_code
.structs_source_code
.into_iter()
.find(|struct_metadata| {
struct_metadata.struct_type == struct_type && struct_metadata.name == struct_name
}) {
None => Err(Report::new(MetadataError).attach_printable(format!(
"Metadata not found for struct with name {} and struct type {}",
struct_name, struct_type
))),
Some(struct_metadata) => Ok(struct_metadata),
}
}
pub fn get_filtered_functions(
function_name: Option<String>,
function_type: Option<FunctionMetadataType>,
) -> MetadataResult<Vec<FunctionSourceCodeMetadata>> {
Ok(BatMetadata::read_metadata()?
.source_code
.functions_source_code
.into_iter()
.filter(|function_metadata| {
if function_name.is_some()
&& function_name.clone().unwrap() != function_metadata.name
{
return false;
};
if function_type.is_some()
&& function_type.unwrap() != function_metadata.function_type
{
return false;
};
true
})
.collect::<Vec<_>>())
}
pub fn get_filtered_traits(
trait_name: Option<String>,
trait_type: Option<TraitMetadataType>,
) -> MetadataResult<Vec<TraitSourceCodeMetadata>> {
Ok(BatMetadata::read_metadata()?
.source_code
.traits_source_code
.into_iter()
.filter(|trait_metadata| {
if trait_name.is_some() && trait_name.clone().unwrap() != trait_metadata.name {
return false;
};
if trait_type.is_some() && trait_type.unwrap() != trait_metadata.trait_type {
return false;
};
true
})
.collect::<Vec<_>>())
}
pub fn get_filtered_enums(
trait_name: Option<String>,
trait_type: Option<EnumMetadataType>,
) -> MetadataResult<Vec<EnumSourceCodeMetadata>> {
Ok(BatMetadata::read_metadata()?
.source_code
.enums_source_code
.into_iter()
.filter(|enum_metadata| {
if trait_name.is_some() && trait_name.clone().unwrap() != enum_metadata.name {
return false;
};
if trait_type.is_some() && trait_type.unwrap() != enum_metadata.enum_type {
return false;
};
true
})
.collect::<Vec<_>>())
}
}
#[derive(
Debug,
PartialEq,
Clone,
Copy,
Default,
strum_macros::Display,
strum_macros::EnumIter,
Serialize,
Deserialize,
)]
pub enum BatMetadataType {
#[default]
Struct,
Function,
Trait,
Enum,
}
impl BatMetadataType {
pub fn prompt_metadata_type_selection() -> Result<Self, MetadataError> {
let metadata_types_vec = BatMetadataType::get_type_vec();
let metadata_types_colorized_vec = BatMetadataType::get_colorized_type_vec(true);
let prompt_text = format!("Please select the {}", "Metadata type".bright_purple());
let selection =
BatDialoguer::select(prompt_text, metadata_types_colorized_vec, None).unwrap();
let metadata_type_selected = &metadata_types_vec[selection];
Ok(*metadata_type_selected)
}
}
pub trait BatMetadataParser<U>
where
Self: Sized + Clone,
U: BatEnumerator,
{
fn name(&self) -> String;
fn path(&self) -> String;
fn metadata_id(&self) -> MetadataId;
fn start_line_index(&self) -> usize;
fn end_line_index(&self) -> usize;
fn metadata_sub_type(&self) -> U;
fn get_bat_metadata_type() -> BatMetadataType;
fn metadata_name() -> String;
fn value_to_vec_string(value: Value) -> MetadataResult<Vec<String>> {
Ok(value
.as_array()
.ok_or(MetadataError)
.into_report()?
.iter()
.map(|val| val.as_str().ok_or(MetadataError).into_report())
.collect::<Result<Vec<_>, MetadataError>>()?
.into_iter()
.map(|val| val.to_string())
.collect::<Vec<_>>())
}
fn new(
path: String,
name: String,
metadata_sub_type: U,
start_line_index: usize,
end_line_index: usize,
metadata_id: MetadataId,
) -> Self;
fn create_metadata_id() -> String {
let s: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(30)
.map(char::from)
.collect();
s
}
fn to_source_code_parser(&self, optional_name: Option<String>) -> SourceCodeParser {
SourceCodeParser::new(
if let Some(function_name) = optional_name {
function_name
} else {
self.name()
},
self.path(),
self.start_line_index(),
self.end_line_index(),
)
}
fn create_metadata_from_dir_entry(entry: DirEntry) -> Result<Vec<Self>, MetadataError>;
}
impl BatEnumerator for BatMetadataType {}
#[derive(Debug, PartialEq, Clone, Copy, strum_macros::Display, strum_macros::EnumIter)]
pub enum BatMetadataMarkdownContent {
Path,
Name,
Type,
StartLineIndex,
EndLineIndex,
MetadataId,
}
impl BatMetadataMarkdownContent {
pub fn get_prefix(&self) -> String {
format!("- {}:", self.to_snake_case())
}
pub fn to_snake_case(&self) -> String {
self.to_string().to_snake_case()
}
pub fn get_info_section_content<T: Display>(&self, content_value: T) -> String {
format!("- {}: {}", self.to_snake_case(), content_value)
}
}