use crate::transition::Transition;
pub const MAIN_ROUTE_INDEX: usize = 0;
#[derive(Debug)]
pub struct Map {
pub routes: Vec<Route>,
pub capture_groups: Vec<Option<String>>,
}
#[derive(Debug)]
pub struct Route {
pub nodes: Vec<Node>,
pub entry_node_index: usize,
pub exit_node_index: usize,
pub is_fixed_matching_begin_point: bool,
}
#[derive(Debug)]
pub struct Node {
pub path: Vec<Path>,
}
#[derive(Debug)]
pub struct Path {
pub transition: Transition,
pub target_node_index: usize,
}
impl Map {
pub fn new() -> Self {
Map {
routes: vec![],
capture_groups: vec![],
}
}
pub fn create_route(&mut self) -> usize {
let route = Route {
nodes: vec![],
entry_node_index: 0,
exit_node_index: 0,
is_fixed_matching_begin_point: false,
};
let idx = self.routes.len();
self.routes.push(route);
idx
}
pub fn create_capture_group(&mut self, name: Option<String>) -> usize {
let idx = self.capture_groups.len();
self.capture_groups.push(name);
idx
}
pub fn get_capture_group_index_by_name(&self, name: &str) -> Option<usize> {
self.capture_groups.iter().position(|item| match item {
Some(n) => n == name,
None => false,
})
}
pub fn get_capture_group_name_by_index(&self, index: usize) -> Option<&str> {
let opt_name = &self.capture_groups[index];
if let Some(name) = opt_name {
Some(name.as_str())
} else {
None
}
}
pub fn get_debug_text(&self) -> String {
let mut buffer = vec![];
if self.routes.len() == 1 {
buffer.push(self.routes[0].get_debug_text());
} else {
for (route_index, route) in self.routes.iter().enumerate() {
buffer.push(format!("= ${}", route_index));
buffer.push(route.get_debug_text());
}
}
for (capture_group_index, opt_capture_group_name) in
self.capture_groups.iter().enumerate()
{
let s = if let Some(name) = &opt_capture_group_name {
format!("# {{{}}}, {}", capture_group_index, name)
} else {
format!("# {{{}}}", capture_group_index)
};
buffer.push(s);
}
buffer.join("\n")
}
}
impl Default for Map {
fn default() -> Self {
Self::new()
}
}
impl Route {
pub fn create_node(&mut self) -> usize {
let node = Node { path: vec![] };
let idx = self.nodes.len();
self.nodes.push(node);
idx
}
pub fn create_path(
&mut self,
source_node_index: usize,
target_node_index: usize,
transition: Transition,
) -> usize {
let transition_item = Path {
transition,
target_node_index,
};
let idx = self.nodes[source_node_index].path.len();
self.nodes[source_node_index].path.push(transition_item);
idx
}
pub fn get_debug_text(&self) -> String {
let mut buffer = vec![];
for (node_index, node) in self.nodes.iter().enumerate() {
let prefix = if node_index == self.entry_node_index {
'>'
} else if node_index == self.exit_node_index {
'<'
} else {
'-'
};
let s = format!("{} {}", prefix, node_index);
buffer.push(s);
for transition_item in &node.path {
let s = format!(
" -> {}, {}",
transition_item.target_node_index, transition_item.transition
);
buffer.push(s);
}
}
buffer.join("\n")
}
}
pub struct Component {
pub in_node_index: usize,
pub out_node_index: usize,
}
impl Component {
pub fn new(in_node_index: usize, out_node_index: usize) -> Self {
Component {
in_node_index,
out_node_index,
}
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::{assert_eq, assert_str_eq};
use crate::{
object_file::Map,
transition::{CharTransition, Transition},
};
#[test]
fn test_object_file_create_route() {
let mut object = Map::new();
{
let route_index = object.create_route();
let route = &mut object.routes[route_index];
let _idx0 = route.create_node();
assert_str_eq!(
route.get_debug_text(),
"\
> 0"
);
let idx1 = route.create_node();
let _idx2 = route.create_node();
let idx3 = route.create_node();
route.entry_node_index = idx1;
route.exit_node_index = idx3;
assert_str_eq!(
route.get_debug_text(),
"\
- 0
> 1
- 2
< 3"
);
}
{
let route_index = object.create_route();
let route = &mut object.routes[route_index];
let _idx0 = route.create_node();
assert_str_eq!(
object.get_debug_text(),
"\
= $0
- 0
> 1
- 2
< 3
= $1
> 0"
);
}
}
#[test]
fn test_object_file_create_capture_group() {
let mut object = Map::new();
let route_index = object.create_route();
let route = &mut object.routes[route_index];
route.create_node();
route.create_node();
object.create_capture_group(None);
object.create_capture_group(Some("foo".to_owned()));
object.create_capture_group(None);
assert_str_eq!(
object.get_debug_text(),
"\
> 0
- 1
# {0}
# {1}, foo
# {2}"
);
assert_eq!(object.get_capture_group_index_by_name("foo"), Some(1));
assert!(object.get_capture_group_index_by_name("bar").is_none());
}
#[test]
fn test_object_file_create_path() {
let mut object = Map::new();
let route_index = object.create_route();
let route = &mut object.routes[route_index];
let node_idx0 = route.create_node();
let node_idx1 = route.create_node();
let node_idx2 = route.create_node();
let node_idx3 = route.create_node();
let trans_idx0 = route.create_path(
node_idx0,
node_idx1,
Transition::Char(CharTransition::new('a')),
);
assert_str_eq!(
route.get_debug_text(),
"\
> 0
-> 1, Char 'a'
- 1
- 2
- 3"
);
assert_eq!(trans_idx0, 0);
let trans_idx1 = route.create_path(
node_idx0,
node_idx2,
Transition::Char(CharTransition::new('b')),
);
let trans_idx2 = route.create_path(
node_idx0,
node_idx3,
Transition::Char(CharTransition::new('c')),
);
assert_str_eq!(
route.get_debug_text(),
"\
> 0
-> 1, Char 'a'
-> 2, Char 'b'
-> 3, Char 'c'
- 1
- 2
- 3"
);
assert_eq!(trans_idx1, 1);
assert_eq!(trans_idx2, 2);
let trans_idx3 = route.create_path(
node_idx1,
node_idx2,
Transition::Char(CharTransition::new('x')),
);
assert_str_eq!(
route.get_debug_text(),
"\
> 0
-> 1, Char 'a'
-> 2, Char 'b'
-> 3, Char 'c'
- 1
-> 2, Char 'x'
- 2
- 3"
);
assert_eq!(trans_idx3, 0);
}
}