1use std::cmp;
17
18use crate::layout::LayoutType;
19use crate::options::Options;
20use crate::uni_smush::uni_smush;
21use crate::rules::v_rule1_smush;
22use crate::rules::v_rule2_smush;
23use crate::rules::v_rule3_smush;
24use crate::rules::v_rule4_smush;
25use crate::rules::v_rule5_smush;
26
27fn can_vertical_smush<'a>(txt1: &'a str, txt2: &'a str, opts: &Options) -> &'a str {
29 if opts.fitting_rules.v_layout == LayoutType::FullWidth {
30 return "invalid";
31 }
32
33 let len = cmp::min(txt1.len(), txt2.len());
34
35 if len == 0 {
36 return "invalid";
37 }
38
39 let mut end_smush = false;
40
41 for (ch1, ch2) in txt1.chars().zip(txt2.chars()) {
42 if ch1 != ' ' && ch2 != ' ' {
43 if opts.fitting_rules.v_layout == LayoutType::Fitting {
44 return "invalid";
45 } else if opts.fitting_rules.v_layout == LayoutType::Smushing {
46 return "end";
47 } else {
48 if ch1 == '|' && ch2 == '|' {
49 continue;
50 }
51 let mut valid_smush = opts.fitting_rules.v_rule1 && v_rule1_smush(ch1, ch2).is_some();
52 valid_smush = !valid_smush && opts.fitting_rules.v_rule2 && v_rule2_smush(ch1, ch2).is_some();
53 valid_smush = !valid_smush && opts.fitting_rules.v_rule3 && v_rule3_smush(ch1, ch2).is_some();
54 valid_smush = !valid_smush && opts.fitting_rules.v_rule4 && v_rule4_smush(ch1, ch2).is_some();
55 end_smush = true;
56 if valid_smush {
57 return "invalid";
58 }
59 }
60 }
61 }
62
63 if end_smush {
64 "end"
65 } else {
66 "valid"
67 }
68}
69
70fn get_vertical_smush_dist(lines1: &Vec<String>, lines2: &Vec<String>, opts: &Options) -> usize {
72 let max_dist = lines1.len();
73 let len1 = lines1.len();
74 let mut cur_dist = 1;
76 let mut result = String::new();
77
78 while cur_dist <= max_dist {
79 let sub_lines1 = &lines1[(len1.saturating_sub(cur_dist))..len1];
80 let sub_lines2 = &lines2[0..cmp::min(max_dist, cur_dist)];
81
82 let slen = sub_lines2.len();
83 result.clear();
84
85 for ii in 0..slen {
86 let ret = can_vertical_smush(&sub_lines1[ii], &sub_lines2[ii], opts);
87 if ret == "end" {
88 result = ret.to_string();
89 } else if ret == "invalid" {
90 result = ret.to_string();
91 break;
92 } else if result.is_empty() {
93 result = "valid".to_string();
94 }
95 }
96
97 if result == "invalid" {
98 cur_dist -= 1;
99 break;
100 }
101
102 if result == "end" {
103 break;
104 }
105
106 if result == "valid" {
107 cur_dist += 1;
108 }
109 }
110
111 cmp::min(max_dist, cur_dist)
112}
113
114fn vertically_smush_lines(line1: &str, line2: &str, opts: &Options) -> String {
116 let mut result = String::new();
117
118 for (ch1, ch2) in line1.chars().zip(line2.chars()) {
119 if ch1 != ' ' && ch2 != ' ' {
120 if opts.fitting_rules.v_layout == LayoutType::Fitting {
121 result.push(uni_smush(ch1, ch2, &' '));
122 } else if opts.fitting_rules.v_layout == LayoutType::Smushing {
123 result.push(uni_smush(ch1, ch2, &' '));
124 } else {
125 let mut valid_smush: Option<char> = None;
126 valid_smush = if opts.fitting_rules.v_rule5 { v_rule5_smush(ch1, ch2) } else { valid_smush };
127 valid_smush = if valid_smush.is_none() && opts.fitting_rules.v_rule1 { v_rule1_smush(ch1, ch2) } else { valid_smush };
128 valid_smush = if valid_smush.is_none() && opts.fitting_rules.v_rule2 { v_rule2_smush(ch1, ch2) } else { valid_smush };
129 valid_smush = if valid_smush.is_none() && opts.fitting_rules.v_rule3 { v_rule3_smush(ch1, ch2) } else { valid_smush };
130 valid_smush = if valid_smush.is_none() && opts.fitting_rules.v_rule4 { v_rule4_smush(ch1, ch2) } else { valid_smush };
131
132 if valid_smush.is_some() {
133 result.push(valid_smush.unwrap());
134 }
135 }
136 } else {
137 result.push(uni_smush(ch1, ch2, &' '));
138 }
139 }
140
141 result
142}
143
144fn vertical_smush(lines1: &Vec<String>, lines2: &Vec<String>, overlap: usize, opts: &Options) -> Vec<String> {
146 let len1 = lines1.len();
147 let len2 = lines2.len();
148 let piece1 = lines1[0..cmp::max(0, len1.saturating_sub(overlap))].to_vec();
149 let piece2_1 = lines1[cmp::max(0, len1.saturating_sub(overlap))..len1].to_vec();
150 let piece2_2 = lines2[0..cmp::min(overlap, len2)].to_vec();
151
152 let mut piece2 = vec![];
153 let len = piece2_1.len();
156 for ii in 0..len {
157 let line: String;
158 if ii >= len2 {
159 line = piece2_1[ii].clone();
160 } else {
161 line = vertically_smush_lines(&piece2_1[ii], &piece2_2[ii], opts);
162 }
163 piece2.push(line);
164 }
165
166 let piece3 = lines2[cmp::min(overlap, len2)..len2].to_vec();
167
168 let mut output = vec![];
169 output.extend_from_slice(&piece1);
170 output.extend_from_slice(&piece2);
171 output.extend_from_slice(&piece3);
172 let scope = output[0].len() - lines1[0].len();
173 pad_lines(&mut output, scope);
174
175 output
176}
177
178
179fn pad_lines(lines: &mut Vec<String>, num_spaces: usize) {
181 let padding: String = " ".repeat(num_spaces);
182 for line in lines.iter_mut() {
183 line.push_str(&padding);
184 }
185}
186
187pub fn smush_vertical_fig_lines(output: &mut Vec<String>, lines: &mut Vec<String>, opts: &Options) -> Vec<String> {
189 let len1 = output[0].len();
190 let len2 = lines[0].len();
191
192 if len1 > len2 {
194 pad_lines(lines, len1 - len2);
195 } else if len2 > len1 {
196 pad_lines(output, len2 - len1);
197 }
198
199 let overlap = get_vertical_smush_dist(output, lines, opts);
201
202 vertical_smush(output, lines, overlap, opts)
203}
204
205