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>>,
pub repetition_counter_count: usize,
}
#[derive(Debug)]
pub struct Route {
pub nodes: Vec<Node>,
pub entry_node_index: usize,
pub exit_node_index: usize,
pub is_fixed_cursor_begin_position: 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![],
repetition_counter_count: 0,
}
}
pub fn create_route(&mut self) -> usize {
let route = Route {
nodes: vec![],
entry_node_index: 0,
exit_node_index: 0,
is_fixed_cursor_begin_position: 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 create_repetition_counter(&mut self) -> usize {
let idx = self.repetition_counter_count;
self.repetition_counter_count += 1;
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: ${}", route_index));
buffer.push(route.get_debug_text());
buffer.push("".to_string());
}
}
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: {{{}}}, name: {}", capture_group_index, name)
} else {
format!("# capture: {{{}}}", 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() {
if node_index == self.entry_node_index {
buffer.push(format!("* node: {} (in)", node_index));
} else if node_index == self.exit_node_index {
buffer.push(format!("* node: {} (out)", node_index));
} else {
buffer.push(format!("* node: {}", node_index));
}
for transition_item in &node.path {
let s = format!(
" - {} -> {}",
transition_item.transition, transition_item.target_node_index
);
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::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(),
"\
* node: 0 (in)"
);
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(),
"\
* node: 0
* node: 1 (in)
* node: 2
* node: 3 (out)"
);
}
{
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(),
"\
= route: $0
* node: 0
* node: 1 (in)
* node: 2
* node: 3 (out)
= route: $1
* node: 0 (in)
"
);
}
}
#[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_string()));
object.create_capture_group(None);
assert_str_eq!(
object.get_debug_text(),
"\
* node: 0 (in)
* node: 1
# capture: {0}
# capture: {1}, name: foo
# capture: {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(),
"\
* node: 0 (in)
- Char 'a' -> 1
* node: 1
* node: 2
* node: 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(),
"\
* node: 0 (in)
- Char 'a' -> 1
- Char 'b' -> 2
- Char 'c' -> 3
* node: 1
* node: 2
* node: 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(),
"\
* node: 0 (in)
- Char 'a' -> 1
- Char 'b' -> 2
- Char 'c' -> 3
* node: 1
- Char 'x' -> 2
* node: 2
* node: 3"
);
assert_eq!(trans_idx3, 0);
}
}