pub mod error;
pub use error::{
ParsingError,
ParsingErrorType,
};
use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::str::FromStr;
#[ derive( Debug ) ]
pub struct EmptyExternalErrorType;
impl Display for EmptyExternalErrorType {
fn fmt( &self, f: &mut Formatter<'_> ) -> std::fmt::Result {
write!( f,
"empty error"
)
}
}
impl Error for EmptyExternalErrorType {
fn source( &self ) -> Option< &( dyn Error + 'static ) > {
None
}
}
pub fn parse_uniform<T,ExternalErrorType>( content: &str ) -> Result<Vec<T>,ParsingError<ExternalErrorType>>
where
T: FromTreeNode<ExternalErrorType = ExternalErrorType>,
ExternalErrorType: Display + Error {
let line_group = LineGroup::new_root( &content );
let root_nodes = line_group.into_data_tree_nodes()?;
let mut result = Vec::new();
for root_node in root_nodes {
let parsed_value = T::from_tree_node( root_node )?;
result.push( parsed_value );
}
Ok( result )
}
pub fn parse_value<T,ExternalErrorType>( source: &str, line: usize ) -> Result<T,ParsingError<ExternalErrorType>>
where
T: FromStr,
ExternalErrorType: Display + Error {
match source.parse() {
Ok( number ) => Ok( number ),
Err( _ ) => Err(
ParsingError::new(
line,
ParsingErrorType::FailedParsingValue,
)
),
}
}
#[derive(Debug,PartialEq)]
pub struct DataTreeNode<'a> {
keyword: &'a str,
header_value: &'a str,
children: Vec<DataTreeNode<'a>>,
source_line: usize,
}
impl<'a> DataTreeNode<'a> {
pub fn get_keyword( &self ) -> &'a str {
self.keyword
}
pub fn get_header_value( &self ) -> &'a str {
self.header_value
}
pub fn get_children( &'a self ) -> &'a Vec<DataTreeNode<'a>> {
&self.children
}
pub fn into_children( self ) -> Vec<DataTreeNode<'a>> {
self.children
}
pub fn get_source_line( &self ) -> usize {
self.source_line
}
pub fn cast_to_type<T>( mut self ) -> DataTreeNode<'a>
where
T: FromTreeNode {
self.keyword = T::get_keyword();
self
}
}
#[derive(PartialEq,Debug)]
pub struct LineGroup<'a> {
text: &'a str,
line_offset: usize,
}
impl<'a> LineGroup<'a> {
pub fn new_root( text: &'a str ) -> LineGroup<'a> {
LineGroup{
text,
line_offset: 0,
}
}
pub fn new_child(
&self,
start_index: usize,
end_index: usize,
) -> LineGroup<'a> {
let nested_text = &self.text[ start_index .. end_index ];
let newlines_count_before =
count_newline_characters( &self.text[ 0 .. start_index ] );
LineGroup{
text: nested_text,
line_offset: self.line_offset + newlines_count_before,
}
}
pub fn into_data_tree_nodes<ExternalErrorType>( self ) ->
Result<Vec<DataTreeNode<'a>>,ParsingError<ExternalErrorType>>
where
ExternalErrorType: Display + Error {
let self_line = self.line_offset;
let outer_line_groups = split_into_outer_blocks( self )?;
let mut result_nodes: Vec<DataTreeNode<'a>> = Vec::new();
for outer_line_group in outer_line_groups.into_iter() {
let Some( colon_position ) = outer_line_group.text.find( ':' )
else {
return Err( ParsingError::new(
outer_line_group.line_offset,
ParsingErrorType::MissingColon
) );
};
let header_value_end_position =
match outer_line_group.text.find( '{' ) {
Some( braket_position ) => braket_position,
None => match outer_line_group.text.find( '\n' ) {
Some( newline_position ) => newline_position,
None => outer_line_group.text.len()
}
};
let mut result = DataTreeNode {
keyword: outer_line_group.text[ 0 .. colon_position ].trim(),
header_value: outer_line_group.text[
colon_position + 1 ..
header_value_end_position
].trim(),
children: Vec::new(),
source_line: self_line,
};
if let Some( first_newline_position ) = outer_line_group.text.find( '\n' ) {
let last_newline_position = outer_line_group.text.rfind( '\n' ).unwrap();
if first_newline_position < last_newline_position {
let inner_line_groups =
split_into_outer_blocks(
outer_line_group.new_child(
first_newline_position + 1,
last_newline_position,
)
)?;
for inner_line_group in inner_line_groups.into_iter() {
result.children.append( &mut inner_line_group.into_data_tree_nodes()? );
}
}
}
result_nodes.push( result );
}
Ok( result_nodes )
}
pub fn get_text( &self ) -> &'a str {
self.text
}
pub fn get_line_offset( &self ) -> usize {
self.line_offset
}
}
fn count_newline_characters( source: &str ) -> usize {
source.chars()
.filter( | c | c.eq( &'\n' ) )
.count()
}
fn find_character_offsets_of_outer_blocks<ExternalErrorType>( source: &LineGroup )
-> Result<Vec<usize>,ParsingError<ExternalErrorType>>
where
ExternalErrorType: Display + Error {
let mut character_offsets: Vec<usize> = Vec::new();
let mut opened_brakets_count: isize = 0;
let mut character_offset_of_line: usize = 0;
for ( line_offset, line ) in source.text.split( '\n' ).enumerate() {
let current_global_line = line_offset + source.line_offset;
if opened_brakets_count == 0 {
character_offsets.push( character_offset_of_line );
}
let has_left_braket = line.contains( '{' );
let has_right_braket = line.contains( '}' );
if has_left_braket && has_right_braket {
return Err(
ParsingError::new(
current_global_line,
ParsingErrorType::BothBraketsInLine
)
);
}
else if has_left_braket {
opened_brakets_count += 1;
}
else if has_right_braket {
opened_brakets_count -= 1;
}
if opened_brakets_count < 0 {
return Err(
ParsingError::new(
current_global_line,
ParsingErrorType::IsolatedClosingBraket
)
);
}
character_offset_of_line += line.len() + 1;
}
Ok( character_offsets )
}
fn split_into_outer_blocks<'a, ExternalErrorType>( source: LineGroup<'a> )
-> Result<Vec<LineGroup<'a>>,ParsingError<ExternalErrorType>>
where
ExternalErrorType: Display + Error {
let mut untrimmed_result: Vec<LineGroup> = Vec::new();
let character_offsets_of_outer_blocks =
find_character_offsets_of_outer_blocks( &source )?;
let mut character_offset_iter =
character_offsets_of_outer_blocks.into_iter();
let Some( mut previous_character_offset ) =
character_offset_iter.next() else {
return Ok( Vec::new() );
};
for current_character_offset in character_offset_iter {
untrimmed_result.push(
source.new_child(
previous_character_offset,
current_character_offset
)
);
previous_character_offset = current_character_offset;
}
untrimmed_result.push(
source.new_child(
previous_character_offset,
source.text.len()
)
);
let mut result = Vec::new();
for line_group in untrimmed_result.into_iter() {
let trimmed_text = line_group.text.trim();
if trimmed_text.len() > 0 {
result.push(
LineGroup {
text: trimmed_text,
line_offset: line_group.line_offset
}
);
}
}
Ok( result )
}
pub trait FromTreeNode: Sized {
type ExternalErrorType: Display + Error;
fn get_keyword() -> &'static str;
fn from_tree_node_internal( node: DataTreeNode ) -> Result<Self,ParsingError<Self::ExternalErrorType>>;
fn from_tree_node( node: DataTreeNode ) -> Result<Self,ParsingError<Self::ExternalErrorType>> {
assert_eq!( node.keyword, Self::get_keyword() );
Self::from_tree_node_internal( node )
}
}
#[cfg(test)]
mod test {
use crate::parser::*;
fn get_source_sample() -> &'static str {
r#"abc
def {
ghi {
}
jkl
}
mno
pqr
stu"#
}
#[test]
fn get_child_of_line_group() {
let root = LineGroup::new_root( get_source_sample() );
assert_eq!( root.line_offset, 0 );
let child = root.new_child( 14, 17 );
assert_eq!( child.text, "ghi" );
assert_eq!( child.line_offset, 2 );
}
#[test]
#[should_panic]
fn find_line_offsets_of_outer_blocks_errs_on_two_brakets() {
let source = "abc{}def";
let _groups = match find_character_offsets_of_outer_blocks::<EmptyExternalErrorType>(
&LineGroup::new_root( source )
) {
Ok( val ) => val,
Err( error ) => panic!( "{}", error ),
};
}
#[test]
#[should_panic]
fn find_line_offsets_of_outer_blocks_errs_on_isolated_closing_braket() {
let source = "abc\ndef{\nghi\njkl\n}\nmno}";
let _groups = match find_character_offsets_of_outer_blocks::<EmptyExternalErrorType>(
&LineGroup::new_root( source )
) {
Ok( val ) => val,
Err( error ) => panic!( "{}", error ),
};
}
#[test]
fn find_line_offsets_of_outer_blocks_works() {
let source = get_source_sample();
let groups = match find_character_offsets_of_outer_blocks::<EmptyExternalErrorType>(
&LineGroup::new_root( source )
) {
Ok( val ) => val,
Err( error ) => panic!( "{}", error ),
};
assert_eq!( groups, vec![ 0, 4, 36, 40, 44 ] );
}
#[test]
fn split_into_outer_blocks_works() {
let source = LineGroup::new_root(
get_source_sample()
);
let line_groups = match split_into_outer_blocks::<EmptyExternalErrorType>( source ) {
Ok( val ) => val,
Err( error ) => panic!( "{}", error ),
};
assert_eq!(
line_groups,
vec![
LineGroup {
text: "abc",
line_offset: 0
},
LineGroup {
text: "def {\n ghi {\n }\n jkl\n}",
line_offset: 1
},
LineGroup {
text: "mno",
line_offset: 6
},
LineGroup {
text: "pqr",
line_offset: 7
},
LineGroup {
text: "stu",
line_offset: 8
}
]
);
}
#[test]
fn into_data_tree_nodes_when_end_on_next_line() {
let source = "abc:def{\n}";
let _ = LineGroup::new_root( source ).into_data_tree_nodes::<EmptyExternalErrorType>();
}
fn get_data_file_example() -> &'static str {
r#"abc: def {
ghi: jkl
mno: pqr {
stu: vwx
yza: bcd
}
efg: hij
}
"#
}
#[test]
fn create_data_tree_node<'a>() {
let source = LineGroup::new_root(
get_data_file_example()
);
let data_tree_nodes = match source.into_data_tree_nodes::<EmptyExternalErrorType>() {
Ok( val ) => val,
Err( error ) => panic!( "{}", error ),
};
assert_eq!( data_tree_nodes.len(), 1 );
let root_node = &data_tree_nodes[ 0 ];
assert_eq!(
root_node,
&DataTreeNode{
keyword: "abc",
header_value: "def",
children: vec![
DataTreeNode{
keyword: "ghi",
header_value: "jkl",
children: vec![],
source_line: 1,
},
DataTreeNode{
keyword: "mno",
header_value: "pqr",
children: vec![
DataTreeNode{
keyword: "stu",
header_value: "vwx",
children: vec![],
source_line: 3,
},
DataTreeNode{
keyword: "yza",
header_value: "bcd",
children: vec![],
source_line: 4,
}
],
source_line: 2,
},
DataTreeNode{
keyword: "efg",
header_value: "hij",
children: vec![],
source_line: 6,
}
],
source_line: 0,
}
);
}
}