Skip to main content

ass_core/analysis/styles/resolved_style/
inheritance.rs

1//! Inheritance-aware construction of `ResolvedStyle` values.
2//!
3//! Provides `ResolvedStyle::from_style_with_parent`, which resolves a style by
4//! inheriting from a parent and applying only the child's overrides.
5
6use super::parsing::{
7    parse_color_with_default, parse_float, parse_font_size, parse_percentage, parse_u16, parse_u8,
8};
9use super::{ResolvedStyle, TextFormatting};
10use crate::{parser::Style, Result};
11use alloc::string::ToString;
12
13impl<'a> ResolvedStyle<'a> {
14    /// Create resolved style with inheritance from parent
15    ///
16    /// # Arguments
17    ///
18    /// * `style` - Style definition with possible overrides
19    /// * `parent` - Parent style to inherit from
20    ///
21    /// # Returns
22    ///
23    /// Resolved style inheriting parent properties with child overrides
24    ///
25    /// # Errors
26    ///
27    /// Returns an error if style parsing fails
28    #[allow(clippy::cognitive_complexity)]
29    pub fn from_style_with_parent(style: &'a Style<'a>, parent: &Self) -> Result<Self> {
30        // Start with parent properties
31        let mut resolved = parent.clone();
32
33        // Update name to child's name
34        resolved.name = style.name;
35
36        // Override properties that are not empty/default in child
37        if !style.fontname.is_empty() {
38            resolved.font_name = style.fontname.to_string();
39        }
40
41        if !style.fontsize.is_empty() && style.fontsize != "0" {
42            resolved.font_size = parse_font_size(style.fontsize)?;
43        }
44
45        if !style.primary_colour.is_empty() {
46            resolved.primary_color = parse_color_with_default(style.primary_colour)?;
47        }
48
49        if !style.secondary_colour.is_empty() {
50            resolved.secondary_color = parse_color_with_default(style.secondary_colour)?;
51        }
52
53        if !style.outline_colour.is_empty() {
54            resolved.outline_color = parse_color_with_default(style.outline_colour)?;
55        }
56
57        if !style.back_colour.is_empty() {
58            resolved.back_color = parse_color_with_default(style.back_colour)?;
59        }
60
61        // For formatting flags, only override if value is non-empty
62        let mut formatting = resolved.formatting;
63        if !style.bold.is_empty() {
64            if style.bold == "0" {
65                formatting &= !TextFormatting::BOLD;
66            } else if style.bold == "1" {
67                formatting |= TextFormatting::BOLD;
68            }
69        }
70        if !style.italic.is_empty() {
71            if style.italic == "0" {
72                formatting &= !TextFormatting::ITALIC;
73            } else if style.italic == "1" {
74                formatting |= TextFormatting::ITALIC;
75            }
76        }
77        if !style.underline.is_empty() {
78            if style.underline == "0" {
79                formatting &= !TextFormatting::UNDERLINE;
80            } else if style.underline == "1" {
81                formatting |= TextFormatting::UNDERLINE;
82            }
83        }
84        if !style.strikeout.is_empty() {
85            if style.strikeout == "0" {
86                formatting &= !TextFormatting::STRIKE_OUT;
87            } else if style.strikeout == "1" {
88                formatting |= TextFormatting::STRIKE_OUT;
89            }
90        }
91        resolved.formatting = formatting;
92
93        if !style.scale_x.is_empty() && style.scale_x != "100" {
94            resolved.scale_x = parse_percentage(style.scale_x)?;
95        }
96
97        if !style.scale_y.is_empty() && style.scale_y != "100" {
98            resolved.scale_y = parse_percentage(style.scale_y)?;
99        }
100
101        if !style.spacing.is_empty() && style.spacing != "0" {
102            resolved.spacing = parse_float(style.spacing)?;
103        }
104
105        if !style.angle.is_empty() && style.angle != "0" {
106            resolved.angle = parse_float(style.angle)?;
107        }
108
109        if !style.border_style.is_empty() {
110            resolved.border_style = parse_u8(style.border_style)?;
111        }
112
113        if !style.outline.is_empty() && style.outline != "0" {
114            resolved.outline = parse_float(style.outline)?;
115        }
116
117        if !style.shadow.is_empty() && style.shadow != "0" {
118            resolved.shadow = parse_float(style.shadow)?;
119        }
120
121        if !style.alignment.is_empty() {
122            resolved.alignment = parse_u8(style.alignment)?;
123        }
124
125        if !style.margin_l.is_empty() {
126            resolved.margin_l = parse_u16(style.margin_l)?;
127        }
128
129        if !style.margin_r.is_empty() {
130            resolved.margin_r = parse_u16(style.margin_r)?;
131        }
132
133        // Handle margin inheritance
134        if let (Some(t), Some(b)) = (style.margin_t, style.margin_b) {
135            if !t.is_empty() {
136                resolved.margin_t = parse_u16(t)?;
137            }
138            if !b.is_empty() {
139                resolved.margin_b = parse_u16(b)?;
140            }
141        } else if !style.margin_v.is_empty() && style.margin_v != "0" {
142            let margin_v = parse_u16(style.margin_v)?;
143            resolved.margin_t = margin_v;
144            resolved.margin_b = margin_v;
145        }
146        // If margin_v is empty or "0", keep inherited margins
147
148        if !style.encoding.is_empty() {
149            resolved.encoding = parse_u8(style.encoding)?;
150        }
151
152        // Recalculate complexity score
153        resolved.complexity_score = Self::calculate_complexity(&resolved);
154
155        Ok(resolved)
156    }
157}