1use std::fmt;
30
31pub struct ContainerBuilder<C> {
32 available: usize,
33 margin_between: usize,
34 children: Vec<Child<C>>,
35}
36
37#[derive(Debug, Clone, Copy, Default)]
38pub enum Optionality {
39 #[default]
40 Required,
41 Optional {
42 priority: usize, },
44}
45
46pub struct Child<C> {
47 content: C,
48 constraints: ChildConstraints,
49 size: Option<usize>, }
51
52#[derive(Debug, Clone, Copy)]
53pub struct ChildConstraints {
54 pub min: usize,
55 pub max: Option<usize>,
56 pub optionality: Optionality,
57 pub grow: f64,
58}
59
60impl Default for ChildConstraints {
61 fn default() -> Self {
62 ChildConstraints {
63 min: 0,
64 max: None,
65 optionality: Optionality::default(),
66 grow: 1.0,
67 }
68 }
69}
70
71pub struct Container<C> {
72 pub children: Vec<Child<C>>,
73}
74
75#[derive(Debug, Clone)]
76pub enum Error {
77 NotEnoughSpace,
78}
79impl std::error::Error for Error {}
80impl fmt::Display for Error {
81 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
82 match self {
83 Error::NotEnoughSpace => write!(f, "Not enough space"),
84 }
85 }
86}
87
88impl ChildConstraints {}
89
90impl<C> ContainerBuilder<C> {
91 pub fn with_available(available: usize) -> Self {
92 ContainerBuilder {
93 available,
94 children: Vec::new(),
95 margin_between: 0,
96 }
97 }
98 pub fn with_margin_between(mut self, margin: usize) -> Self {
99 self.margin_between = margin;
100 self
101 }
102 pub fn with(mut self, child: Child<C>) -> Self {
103 self.add(child);
104 self
105 }
106 pub fn add(&mut self, child: Child<C>) {
107 self.children.push(child);
108 }
109 pub fn build(self) -> Result<Container<C>, Error> {
110 let Self {
111 mut available,
112 mut children,
113 margin_between,
114 } = self;
115
116 let mut added_children = 0;
119 for child in &mut children {
120 child.size = if child.is_optional() {
121 None
122 } else {
123 let margin = if added_children > 0 {
124 margin_between
125 } else {
126 0
127 };
128 if child.constraints.min + margin > available {
129 return Err(Error::NotEnoughSpace);
130 }
131 available -= child.constraints.min;
132 available -= margin;
133 added_children += 1;
134 Some(child.constraints.min)
135 };
136 }
137
138 let mut optional_children = children
141 .iter_mut()
142 .filter(|c| c.is_optional())
143 .collect::<Vec<_>>();
144 optional_children.sort_by_key(|c| {
145 std::cmp::Reverse(match c.constraints.optionality {
146 Optionality::Optional { priority } => priority,
147 _ => 0,
148 })
149 });
150 for child in optional_children {
151 let margin = if added_children > 0 {
152 margin_between
153 } else {
154 0
155 };
156 if child.constraints.min + margin > available {
157 continue;
158 }
159 available -= child.constraints.min;
160 available -= margin;
161 added_children += 1;
162 child.size = Some(child.constraints.min);
163 }
164
165 let mut growths = vec![0.0; children.len()];
167 let mut sum_growths = 0.0;
168 for (i, child) in children.iter().enumerate() {
169 let Some(size) = child.size else {
170 continue;
171 };
172 growths[i] = child.constraints.grow
173 * (match child.constraints.max {
174 None => available,
175 Some(max) => max - size,
176 } as f64);
177 sum_growths += growths[i];
178 }
179 for i in 0..children.len() {
180 let Some(size) = children[i].size else {
181 continue;
182 };
183 let growth = growths[i] as f64 * (available as f64 / sum_growths);
184 available -= growth as usize;
185 children[i].size = Some(size + growth as usize);
186 }
187
188 while available > 0 {
190 let mut given = 0;
191 for child in &mut children {
192 let Some(size) = child.size else {
193 continue;
194 };
195 if child.constraints.max.map_or(true, |max| size < max) {
196 child.size = Some(size + 1);
197 given += 1;
198 available -= 1;
199 if available == 0 {
200 break;
201 }
202 }
203 }
204 if given == 0 {
205 break;
206 }
207 }
208
209 let con = Container { children };
210 Ok(con)
211 }
212}
213
214impl<C> Child<C> {
215 pub fn new(content: C) -> Self {
216 let constraints = ChildConstraints::default();
217 Child {
218 content,
219 constraints,
220 size: None,
221 }
222 }
223 pub fn content(&self) -> &C {
224 &self.content
225 }
226 pub fn optional(self) -> Self {
227 self.optional_with_priority(0)
228 }
229 pub fn optional_with_priority(mut self, priority: usize) -> Self {
230 self.constraints.optionality = Optionality::Optional { priority };
231 self
232 }
233 pub fn with_min(mut self, min: usize) -> Self {
234 self.constraints.min = min;
235 self
236 }
237 pub fn with_max(mut self, max: usize) -> Self {
238 self.constraints.max = Some(max);
239 self
240 }
241 pub fn clamp(mut self, min: usize, max: usize) -> Self {
242 self.constraints.min = min;
243 self.constraints.max = Some(max);
244 self
245 }
246 pub fn with_size(mut self, size: usize) -> Self {
247 self.constraints.min = size;
248 self.constraints.max = Some(size);
249 self
250 }
251 pub fn with_grow(mut self, grow: f64) -> Self {
252 self.constraints.grow = grow;
253 self
254 }
255 pub fn constraints(&self) -> ChildConstraints {
256 self.constraints
257 }
258 fn is_optional(&self) -> bool {
259 matches!(self.constraints.optionality, Optionality::Optional { .. })
260 }
261 pub fn size(&self) -> Option<usize> {
264 self.size
265 }
266}
267
268impl<C> Container<C> {
269 pub fn builder_in(available: usize) -> ContainerBuilder<C> {
270 ContainerBuilder::with_available(available)
271 }
272 pub fn sizes(&self) -> Vec<usize> {
275 self.children
276 .iter()
277 .map(|sc| sc.size.unwrap_or(0))
278 .collect()
279 }
280 pub fn children(&self) -> &[Child<C>] {
281 &self.children
282 }
283 pub fn to_children(self) -> Vec<Child<C>> {
284 self.children
285 }
286}