1pub mod errors;
3
4use std::cmp::Ordering;
5
6use errors::CompileError;
7use unicode_width::UnicodeWidthStr;
8
9#[derive(Debug, Clone)]
27pub struct CompileConfig {
28 pub char_width: CharWidthMode,
30}
31
32impl CompileConfig {
33 pub const DEFAULT: CompileConfig = CompileConfig {
35 char_width: CharWidthMode::Mono,
36 };
37}
38
39#[derive(Debug, Clone)]
45pub enum CharWidthMode {
46 Mono,
48 Half,
50 Full,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
58pub struct CodeCharacter {
59 pub char: String,
61 pub x: usize,
63 pub len: usize,
65}
66
67#[derive(Debug, Clone, PartialEq, Eq)]
91pub struct SplitedCode {
92 body: Vec<Vec<CodeCharacter>>,
93}
94
95impl SplitedCode {
96 pub fn get(&self, x: usize, y: usize) -> Option<CodeCharacter> {
100 self.body.get(y)?.iter().find(|cc| cc.x == x).cloned()
101 }
102
103 pub fn get_slice_of_line(&self, x_min_exclusive: usize, x_max_exclusive: usize, y: usize) -> Option<String> {
107 let (mut index, _) = self.body.get(y)?.iter().enumerate().find(|(_index, cc)| cc.x == x_min_exclusive)?;
108 let mut return_str = "".to_string();
109
110 index += 1;
111
112 while let Some(cc) = self.body.get(y)?.get(index) {
113 match cc.x.cmp(&x_max_exclusive) {
114 Ordering::Equal => break,
115 Ordering::Greater => return None,
116 Ordering::Less => {}
117 }
118 return_str += &cc.char;
119
120 index += 1;
121 }
122
123 Some(return_str.to_string())
124 }
125
126 pub fn left_x(&self, x: usize, y: usize) -> Option<usize> {
130 let index =
131 self.body.get(y)?.iter().enumerate().find_map(|(index, cc)| if cc.x == x { Some(index) } else { None })?;
132 self.body.get(y)?.get(index - 1).map(|cc| cc.x)
133 }
134
135 pub fn right_x(&self, x: usize, y: usize) -> Option<usize> {
139 let index =
140 self.body.get(y)?.iter().enumerate().find_map(|(index, cc)| if cc.x == x { Some(index) } else { None })?;
141 self.body.get(y)?.get(index + 1).map(|cc| cc.x)
142 }
143
144 fn new() -> Self {
145 SplitedCode { body: vec![vec![]] }
146 }
147
148 fn append(&mut self, char: &str, char_width: &CharWidthMode) {
149 let now_line = self.body.last_mut().unwrap();
150
151 let x = if now_line.is_empty() {
152 0
153 } else {
154 now_line.last().unwrap().x + now_line.last().unwrap().len
155 };
156
157 let width = char.width();
158 let width_cjk = char.width_cjk();
159
160 now_line.push(CodeCharacter {
161 char: char.to_string(),
162 x,
163 len: match char_width {
164 CharWidthMode::Mono => 1,
165 CharWidthMode::Half => width,
166 CharWidthMode::Full => width_cjk,
167 },
168 });
169 }
170 fn new_line(&mut self) {
171 self.body.push(vec![]);
172 }
173
174 pub fn len_y(&self) -> usize {
176 self.body.len()
177 }
178
179 pub fn enumurate_x(&self, y: usize) -> Box<dyn std::iter::Iterator<Item = usize> + '_> {
181 Box::new(self.body[y].iter().map(|cc| cc.x))
182 }
183}
184
185#[derive(Debug, Clone, PartialEq, Eq)]
216pub struct CompilingBlock {
217 pub proc_name: String,
219 pub x: usize,
221 pub y: usize,
223 pub width: usize,
225 pub height: usize,
227 pub block_plug: Option<BlockPlug>,
229 pub connect_from: Option<Edge>,
233 pub arg_plugs: Vec<ArgPlug>,
235 pub args: Vec<Edge>,
239}
240
241#[derive(Debug, Clone, PartialEq, Eq)]
245pub struct ArgPlug {
246 pub x: usize,
248 pub y: usize,
250 pub expand: bool,
252 pub ori: Orientation,
254}
255
256#[derive(Debug, Clone, PartialEq, Eq)]
260pub struct EdgeFragment {
261 pub x: usize,
263 pub y: usize,
265 pub ori: Orientation,
267}
268
269#[derive(Debug, Clone, PartialEq, Eq)]
273pub struct Edge {
274 pub block_index_of_arg_plug: usize,
276 pub arg_plug_info: ArgPlug,
278 pub fragments: Vec<EdgeFragment>,
280 pub block_index_of_block_plug: usize,
282}
283
284#[derive(Debug, Clone, PartialEq, Eq)]
288pub struct BlockPlug {
289 pub x: usize,
291 pub y: usize,
293 pub quote: QuoteStyle,
295}
296
297#[derive(Debug, Clone, Copy, PartialEq, Eq)]
299pub enum Orientation {
300 Up,
302 Left,
304 Right,
306 Down,
308}
309
310#[derive(Debug, Clone, PartialEq, Eq)]
312pub enum QuoteStyle {
313 Quote,
315 Closure,
317 None,
319}
320
321fn find_a_block(code: &SplitedCode, x: usize, y: usize, _config: &CompileConfig) -> Option<CompilingBlock> {
322 let cc = |dx: usize, dy: usize| -> Option<CodeCharacter> { code.get(x + dx, y + dy) };
323 let char = |dx: usize, dy: usize| -> Option<String> { code.get(x + dx, y + dy).map(|x| x.char.clone()) };
324
325 let char_is_in = |dx: usize, dy: usize, targets: &[&str]| -> Option<bool> {
326 let c = char(dx, dy)?;
327
328 let matched = targets.iter().any(|t| *t == c);
329
330 Some(matched)
331 };
332
333 let mut up_plug = None;
334 let mut arg_plugs: Vec<_> = vec![];
335
336 if char(0, 0)? != "┌" {
337 return None;
338 };
339 let mut width1 = code.right_x(x, y)? - x;
342 while char_is_in(width1, 0, &["─", "┴", "•", "/"])? {
343 if char_is_in(width1, 0, &["┴", "•", "/"])? {
344 if up_plug.is_some() {
345 return None;
346 }
347 match char(width1, 0)?.as_str() {
348 "┴" => {
349 up_plug = Some(BlockPlug {
350 x: x + width1,
351 y,
352 quote: QuoteStyle::None,
353 });
354 }
355 "•" => {
356 up_plug = Some(BlockPlug {
357 x: x + width1,
358 y,
359 quote: QuoteStyle::Quote,
360 });
361 }
362 "/" => {
363 up_plug = Some(BlockPlug {
364 x: x + width1,
365 y,
366 quote: QuoteStyle::Closure,
367 });
368 }
369 _ => {}
370 }
371 }
372
373 width1 += cc(width1, 0)?.len;
374 }
375 if char(width1, 0)? != "┐" {
376 return None;
377 };
378
379 let mut height1 = 1;
380 while char_is_in(width1, height1, &["│", "├", "@"])? {
381 match char(width1, height1)?.as_str() {
382 "├" => {
383 arg_plugs.push(ArgPlug {
384 x: x + width1,
385 y: y + height1,
386 expand: false,
387 ori: Orientation::Right,
388 });
389 }
390 "@" => {
391 arg_plugs.push(ArgPlug {
392 x: x + width1,
393 y: y + height1,
394 expand: true,
395 ori: Orientation::Right,
396 });
397 }
398 _ => {}
399 }
400 height1 += 1;
401 }
402 if char(width1, height1)? != "┘" {
403 return None;
404 };
405
406 let mut under_width1 = code.right_x(x, y + height1)? - x;
407 while char_is_in(under_width1, height1, &["─", "┬", "@"])? {
408 match char(under_width1, height1)?.as_str() {
409 "┬" => {
410 arg_plugs.push(ArgPlug {
411 x: x + under_width1,
412 y: y + height1,
413 expand: false,
414 ori: Orientation::Down,
415 });
416 }
417 "@" => {
418 arg_plugs.push(ArgPlug {
419 x: x + under_width1,
420 y: y + height1,
421 expand: true,
422 ori: Orientation::Down,
423 });
424 }
425 _ => {}
426 }
427 under_width1 += cc(under_width1, height1)?.len;
428 }
429 if char(0, height1)? != "└" || under_width1 != width1 {
430 return None;
431 };
432
433 let mut under_height1 = 1;
434 while char_is_in(0, under_height1, &["│", "┤", "@"])? {
435 if char(0, under_height1)? == "┤" {
436 arg_plugs.push(ArgPlug {
437 x,
438 y: y + under_height1,
439 expand: false,
440 ori: Orientation::Left,
441 });
442 } else if char(0, under_height1)? == "@" {
443 arg_plugs.push(ArgPlug {
444 x,
445 y: y + under_height1,
446 expand: true,
447 ori: Orientation::Left,
448 });
449 }
450 under_height1 += 1;
451 }
452 if under_height1 != height1 {
453 return None;
454 };
455
456 let mut proc_name = "".to_owned();
457
458 for inside_y in 1..height1 {
459 proc_name += code.get_slice_of_line(x, x + width1, y + inside_y)?.trim();
460 proc_name += "\n";
461 }
462
463 arg_plugs.sort_by(|a, b| {
464 if a.x != b.x {
465 a.x.cmp(&b.x)
466 } else if a.x == x {
467 a.y.cmp(&b.y)
468 } else if a.x == x + width1 {
469 b.y.cmp(&a.y)
470 } else {
471 Ordering::Equal
472 }
473 });
474
475 Some(CompilingBlock {
476 proc_name: proc_name.trim().to_owned(),
477 args: vec![],
478 x,
479 y,
480 width: width1 + cc(width1, 0)?.len,
481 height: height1 + 1,
482 block_plug: up_plug,
483 connect_from: None,
484 arg_plugs,
485 })
486}
487
488pub fn find_blocks(splited_code: &SplitedCode, config: &CompileConfig) -> Vec<CompilingBlock> {
492 let mut blocks: Vec<CompilingBlock> = vec![];
493
494 for y in 0..splited_code.len_y() {
495 for x in splited_code.enumurate_x(y) {
496 if let Some(b) = find_a_block(splited_code, x, y, config) {
497 blocks.push(b);
498 }
499 }
500 }
501
502 blocks
503}
504
505fn find_next_edge(code: &SplitedCode, x: &usize, y: &usize, ori: &Orientation) -> Result<EdgeFragment, EdgeFragment> {
506 let update_and_check =
507 |new_x: usize, new_y: usize, up: &str, left: &str, right: &str, down: &str| -> Result<EdgeFragment, EdgeFragment> {
508 let cc = code.get(new_x, new_y).ok_or(EdgeFragment {
509 x: new_x,
510 y: new_y,
511 ori: *ori,
512 })?;
513
514 let t = cc.char;
515 if t == up {
516 Ok(EdgeFragment {
517 x: new_x,
518 y: new_y,
519 ori: Orientation::Up,
520 })
521 } else if t == left {
522 Ok(EdgeFragment {
523 x: new_x,
524 y: new_y,
525 ori: Orientation::Left,
526 })
527 } else if t == right {
528 Ok(EdgeFragment {
529 x: new_x,
530 y: new_y,
531 ori: Orientation::Right,
532 })
533 } else if t == down {
534 Ok(EdgeFragment {
535 x: new_x,
536 y: new_y,
537 ori: Orientation::Down,
538 })
539 } else {
540 Err(EdgeFragment {
541 x: new_x,
542 y: new_y,
543 ori: *ori,
544 })
545 }
546 };
547
548 match ori {
549 Orientation::Up => update_and_check(*x, y - 1, "│", "┐", "┌", ""),
550 Orientation::Left => update_and_check(code.left_x(*x, *y).unwrap_or(*x - 1), *y, "└", "─", "", "┌"),
551 Orientation::Right => update_and_check(
552 code.right_x(*x, *y).unwrap_or(*x + code.get(*x, *y).unwrap().len),
553 *y,
554 "┘",
555 "",
556 "─",
557 "┐",
558 ),
559 Orientation::Down => update_and_check(*x, y + 1, "", "┘", "└", "│"),
560 }
561}
562
563pub fn connect_blocks(
567 code: &SplitedCode,
568 blocks: &mut [CompilingBlock],
569 config: &CompileConfig,
570) -> Result<CompilingBlock, CompileError> {
571 let blocks_cloned = blocks.to_owned();
572
573 let head_candinates: Vec<usize> = blocks
574 .iter()
575 .enumerate()
576 .filter_map(|(i, block)| if block.block_plug.is_some() { None } else { Some(i) })
577 .collect();
578
579 if head_candinates.len() != 1 {
580 return Err(CompileError::NonUniqueStartBlock(Box::new(
581 errors::NonUniqueStartBlockError {
582 candinates: head_candinates.iter().map(|i| blocks[*i].clone()).collect(),
583 },
584 )));
585 }
586 let head = head_candinates[0];
587
588 let mut deferred_connections = Vec::new();
590
591 for (block_index, block) in blocks.iter_mut().enumerate() {
592 for arg_plug in block.arg_plugs.iter() {
593 let ArgPlug { x, y, ori, .. } = arg_plug;
594
595 let mut mut_x = *x;
596 let mut mut_y = *y;
597 let mut mut_ori = *ori;
598
599 let mut fragments = Vec::new();
601
602 loop {
603 match find_next_edge(code, &mut_x, &mut_y, &mut_ori) {
604 Ok(edge) => {
605 mut_x = edge.x;
606 mut_y = edge.y;
607 mut_ori = edge.ori;
608 fragments.push(edge);
609 }
610 Err(edge) => {
611 mut_x = edge.x;
612 mut_y = edge.y;
613 break;
614 }
615 }
616 }
617
618 let (arg_block_index, _) = blocks_cloned
619 .iter()
620 .enumerate()
621 .find(|(_, b)| {
622 if let Some(p) = &b.block_plug {
623 p.x == mut_x && p.y == mut_y
624 } else {
625 false
626 }
627 })
628 .ok_or(CompileError::DanglingArgEdge(Box::new(errors::DanglingArgEdgeError {
629 block_of_arg_plug: block.clone(),
630 arg_plug: arg_plug.clone(),
631 edge_fragments: fragments.clone(),
632 dangling_position: (mut_x, mut_y),
633 })))?;
634
635 let connect_edge = Edge {
636 block_index_of_arg_plug: block_index,
637 arg_plug_info: arg_plug.clone(),
638 fragments,
639 block_index_of_block_plug: arg_block_index,
640 };
641
642 block.args.push(connect_edge.clone());
643
644 deferred_connections.push((arg_block_index, connect_edge.clone()));
646 }
647 }
648
649 for (arg_block_index, connect_edge) in deferred_connections {
650 let block = &mut blocks[arg_block_index];
651 block.connect_from = Some(connect_edge);
652 }
653
654 Ok(blocks[head].clone())
655}
656
657pub fn split_code(code: &Vec<String>, config: &CompileConfig) -> SplitedCode {
661 let mut splited_code = SplitedCode::new();
662
663 for line in code {
664 for char in line.split("") {
665 if !char.is_empty() {
666 splited_code.append(char, &config.char_width);
667 }
668 }
669
670 splited_code.new_line();
671 }
672
673 splited_code
674}
675
676#[cfg(test)]
677mod tests {
678 use crate::compile::{
679 ArgPlug, BlockPlug, CodeCharacter, CompileConfig, CompilingBlock, Edge, EdgeFragment, Orientation, QuoteStyle,
680 SplitedCode,
681 errors::{self, CompileError},
682 find_a_block, find_blocks,
683 };
684
685 use super::{connect_blocks, split_code};
686
687 #[test]
688 fn test_split_code() {
689 let code = vec![" ┌┐".to_owned()];
690 let splited = split_code(&code, &CompileConfig::DEFAULT);
691 let target = SplitedCode {
692 body: vec![
693 vec![
694 CodeCharacter {
695 char: " ".to_owned(),
696 x: 0,
697 len: 1,
698 },
699 CodeCharacter {
700 char: "┌".to_owned(),
701 x: 1,
702 len: 1,
703 },
704 CodeCharacter {
705 char: "┐".to_owned(),
706 x: 2,
707 len: 1,
708 },
709 ],
710 vec![],
711 ],
712 };
713 assert_eq!(splited, target);
714 }
715 #[test]
716 fn test_split_code_cjk() {
717 let mut config = CompileConfig::DEFAULT.clone();
718 config.char_width = crate::compile::CharWidthMode::Full;
719
720 let code = vec![" ┌┐".to_owned()];
721 let splited = split_code(&code, &config);
722 let target = SplitedCode {
723 body: vec![
724 vec![
725 CodeCharacter {
726 char: " ".to_owned(),
727 x: 0,
728 len: 1,
729 },
730 CodeCharacter {
731 char: "┌".to_owned(),
732 x: 1,
733 len: 2,
734 },
735 CodeCharacter {
736 char: "┐".to_owned(),
737 x: 3,
738 len: 2,
739 },
740 ],
741 vec![],
742 ],
743 };
744 assert_eq!(splited, target);
745 }
746
747 #[test]
748 fn test_find_a_block() {
749 let config = CompileConfig::DEFAULT;
750
751 let block = find_a_block(
752 &split_code(
753 &vec![
754 " ".to_owned(),
755 " ┌─────┐ ".to_owned(),
756 " │ abc │ ".to_owned(),
757 " └─────┘ ".to_owned(),
758 " ".to_owned(),
759 ],
760 &config,
761 ),
762 4,
763 1,
764 &config,
765 );
766
767 assert_eq!(
768 Some(CompilingBlock {
769 proc_name: "abc".to_string(),
770 x: 4,
771 y: 1,
772 width: 7,
773 height: 3,
774 block_plug: None,
775 connect_from: None,
776 arg_plugs: vec![],
777 args: vec![]
778 }),
779 block
780 );
781 }
782
783 #[test]
784 fn test_find_a_block_cjk() {
785 let mut config = CompileConfig::DEFAULT.clone();
786 config.char_width = crate::compile::CharWidthMode::Full;
787
788 let block = find_a_block(
789 &split_code(
790 &vec![
791 " ".to_owned(),
792 " ┌───┐ ".to_owned(),
793 " │ abc │ ".to_owned(),
794 " └───┘ ".to_owned(),
795 " ".to_owned(),
796 ],
797 &config,
798 ),
799 4,
800 1,
801 &config,
802 );
803
804 assert_eq!(
805 Some(CompilingBlock {
806 proc_name: "abc".to_string(),
807 x: 4,
808 y: 1,
809 width: 10,
810 height: 3,
811 block_plug: None,
812 connect_from: None,
813 arg_plugs: vec![],
814 args: vec![]
815 }),
816 block
817 );
818 }
819
820 #[test]
821 fn check_find_blocks() {
822 let config = CompileConfig::DEFAULT;
823
824 let blocks = find_blocks(
825 &split_code(
826 &vec![
827 " ".to_owned(),
828 " ┌───────┐".to_owned(),
829 " │ abc │ ".to_owned(),
830 " └───┬───┘ ".to_owned(),
831 " ┌───┴──┐".to_owned(),
832 " │ def │ ".to_owned(),
833 " └──────┘ ".to_owned(),
834 ],
835 &config,
836 ),
837 &config,
838 );
839
840 assert_eq!(
841 vec![
842 CompilingBlock {
843 proc_name: "abc".to_owned(),
844 x: 4,
845 y: 1,
846 width: 9,
847 height: 3,
848 block_plug: None,
849 connect_from: None,
850 arg_plugs: vec![ArgPlug {
851 x: 8,
852 y: 3,
853 expand: false,
854 ori: Orientation::Down
855 }],
856 args: vec![]
857 },
858 CompilingBlock {
859 proc_name: "def".to_owned(),
860 x: 4,
861 y: 4,
862 width: 8,
863 height: 3,
864 block_plug: Some(BlockPlug {
865 x: 8,
866 y: 4,
867 quote: QuoteStyle::None
868 }),
869 connect_from: None,
870 arg_plugs: vec![],
871 args: vec![]
872 }
873 ],
874 blocks
875 );
876 }
877
878 #[test]
879 fn check_find_blocks_half() {
880 let mut config = CompileConfig::DEFAULT.clone();
881 config.char_width = crate::compile::CharWidthMode::Half;
882
883 let blocks = find_blocks(
884 &split_code(
885 &vec![
886 " ".to_owned(),
887 " ┌───────┐".to_owned(),
888 " │ あc │ ".to_owned(),
889 " └───┬───┘ ".to_owned(),
890 " ┌───┴──┐".to_owned(),
891 " │ いf │ ".to_owned(),
892 " └──────┘ ".to_owned(),
893 ],
894 &config,
895 ),
896 &config,
897 );
898
899 assert_eq!(
900 vec![
901 CompilingBlock {
902 proc_name: "あc".to_owned(),
903 x: 4,
904 y: 1,
905 width: 9,
906 height: 3,
907 block_plug: None,
908 connect_from: None,
909 arg_plugs: vec![ArgPlug {
910 x: 8,
911 y: 3,
912 expand: false,
913 ori: Orientation::Down
914 }],
915 args: vec![]
916 },
917 CompilingBlock {
918 proc_name: "いf".to_owned(),
919 x: 4,
920 y: 4,
921 width: 8,
922 height: 3,
923 block_plug: Some(BlockPlug {
924 x: 8,
925 y: 4,
926 quote: QuoteStyle::None
927 }),
928 connect_from: None,
929 arg_plugs: vec![],
930 args: vec![]
931 }
932 ],
933 blocks
934 );
935 }
936
937 #[test]
938 fn check_find_blocks_cjk() {
939 let mut config = CompileConfig::DEFAULT.clone();
940 config.char_width = crate::compile::CharWidthMode::Full;
941
942 let blocks = find_blocks(
943 &split_code(
944 &vec![
945 " ".to_owned(),
946 " ┌────┐".to_owned(),
947 " │ abc │ ".to_owned(),
948 " └─┬──┘ ".to_owned(),
949 " ┌─┴─┐".to_owned(),
950 " │ def │ ".to_owned(),
951 " └───┘ ".to_owned(),
952 ],
953 &config,
954 ),
955 &config,
956 );
957
958 assert_eq!(
959 vec![
960 CompilingBlock {
961 proc_name: "abc".to_owned(),
962 x: 4,
963 y: 1,
964 width: 12,
965 height: 3,
966 block_plug: None,
967 connect_from: None,
968 arg_plugs: vec![ArgPlug {
969 x: 8,
970 y: 3,
971 expand: false,
972 ori: Orientation::Down
973 }],
974 args: vec![]
975 },
976 CompilingBlock {
977 proc_name: "def".to_owned(),
978 x: 4,
979 y: 4,
980 width: 10,
981 height: 3,
982 block_plug: Some(BlockPlug {
983 x: 8,
984 y: 4,
985 quote: QuoteStyle::None
986 }),
987 connect_from: None,
988 arg_plugs: vec![],
989 args: vec![]
990 }
991 ],
992 blocks
993 );
994 }
995
996 #[test]
997 fn two_connect() {
998 let splited_code = split_code(
999 &vec![
1000 " ".to_owned(),
1001 " ┌───────┐".to_owned(),
1002 " │ abc │ ".to_owned(),
1003 " └───┬───┘ ".to_owned(),
1004 " │ ".to_owned(),
1005 " ┌───┴──┐".to_owned(),
1006 " │ def │ ".to_owned(),
1007 " └──────┘ ".to_owned(),
1008 ],
1009 &CompileConfig::DEFAULT,
1010 );
1011
1012 let mut blocks = find_blocks(&splited_code, &CompileConfig::DEFAULT);
1013 let head = connect_blocks(&splited_code, &mut blocks, &CompileConfig::DEFAULT).unwrap();
1014
1015 let arg_edge = Edge {
1016 block_index_of_arg_plug: 0,
1017 arg_plug_info: ArgPlug {
1018 x: 8,
1019 y: 3,
1020 expand: false,
1021 ori: Orientation::Down,
1022 },
1023 fragments: vec![EdgeFragment {
1024 x: 8,
1025 y: 4,
1026 ori: Orientation::Down,
1027 }],
1028 block_index_of_block_plug: 1,
1029 };
1030
1031 assert_eq!(
1032 head,
1033 CompilingBlock {
1034 proc_name: "abc".to_owned(),
1035 x: 4,
1036 y: 1,
1037 width: 9,
1038 height: 3,
1039 block_plug: None,
1040 connect_from: None,
1041 arg_plugs: vec![ArgPlug {
1042 x: 8,
1043 y: 3,
1044 expand: false,
1045 ori: Orientation::Down
1046 }],
1047 args: vec![arg_edge.clone()]
1048 }
1049 );
1050
1051 assert_eq!(
1052 blocks[1],
1053 CompilingBlock {
1054 proc_name: "def".to_owned(),
1055 x: 4,
1056 y: 5,
1057 width: 8,
1058 height: 3,
1059 block_plug: Some(BlockPlug {
1060 x: 8,
1061 y: 5,
1062 quote: QuoteStyle::None
1063 }),
1064 connect_from: Some(arg_edge),
1065 arg_plugs: vec![],
1066 args: vec![]
1067 }
1068 );
1069 }
1070
1071 #[test]
1072 fn error_non_unique_start_block() {
1073 let code = vec![
1074 " ".to_owned(),
1075 " ┌───────┐".to_owned(),
1076 " │ abc │ ".to_owned(),
1077 " └───────┘ ".to_owned(),
1078 " ┌──────┐".to_owned(),
1079 " │ def │ ".to_owned(),
1080 " └──────┘ ".to_owned(),
1081 ];
1082
1083 let splited_code = split_code(&code, &CompileConfig::DEFAULT);
1084 let mut blocks = find_blocks(&splited_code, &CompileConfig::DEFAULT);
1085
1086 let result = connect_blocks(&splited_code, &mut blocks, &CompileConfig::DEFAULT);
1087
1088 assert_eq!(
1089 result,
1090 Err(CompileError::NonUniqueStartBlock(Box::new(
1091 errors::NonUniqueStartBlockError {
1092 candinates: vec![blocks[0].clone(), blocks[1].clone()],
1093 }
1094 )))
1095 );
1096 }
1097
1098 #[test]
1099 fn error_dangling_arg_edge() {
1100 let code = vec![
1101 " ".to_owned(),
1102 " ┌───────┐".to_owned(),
1103 " │ abc │ ".to_owned(),
1104 " └───┬───┘ ".to_owned(),
1105 " │ ".to_owned(),
1106 " ".to_owned(),
1107 " ┌───┴──┐".to_owned(),
1108 " │ def │ ".to_owned(),
1109 " └──────┘ ".to_owned(),
1110 ];
1111
1112 let splited_code = split_code(&code, &CompileConfig::DEFAULT);
1113 let mut blocks = find_blocks(&splited_code, &CompileConfig::DEFAULT);
1114
1115 let result = connect_blocks(&splited_code, &mut blocks, &CompileConfig::DEFAULT);
1116
1117 assert_eq!(
1118 result,
1119 Err(CompileError::DanglingArgEdge(Box::new(errors::DanglingArgEdgeError {
1120 block_of_arg_plug: blocks[0].clone(),
1121 arg_plug: blocks[0].arg_plugs[0].clone(),
1122 edge_fragments: vec![EdgeFragment {
1123 x: 8,
1124 y: 4,
1125 ori: Orientation::Down
1126 }],
1127 dangling_position: (8, 5)
1128 })))
1129 );
1130 }
1131
1132 #[test]
1133 fn ignore_two_block_plug() {
1134 let code = vec![
1135 " ".to_owned(),
1136 " ┌───────┐".to_owned(),
1137 " │ abc │ ".to_owned(),
1138 " └───────┘ ".to_owned(),
1139 " ".to_owned(),
1140 " ┌──┴┴──┐".to_owned(),
1141 " │ def │ ".to_owned(),
1142 " └──────┘ ".to_owned(),
1143 ];
1144
1145 let splited_code = split_code(&code, &CompileConfig::DEFAULT);
1146 let blocks = find_blocks(&splited_code, &CompileConfig::DEFAULT);
1147
1148 assert_eq!(blocks.len(), 1);
1149 }
1150}