microsoft_fast_build/
error.rs1use std::fmt;
2
3#[derive(Debug)]
5pub enum RenderError {
6 UnclosedBinding { expr: String, context: String },
8 UnclosedUnescapedBinding { expr: String, context: String },
10 EmptyBinding { context: String },
12 MissingState { binding: String, context: String },
14 UnclosedDirective { tag: String, context: String },
16 MissingValueAttribute { tag: String, context: String },
18 InvalidRepeatExpression { expr: String, context: String },
20 NotAnArray { binding: String, context: String },
22 JsonParse { message: String },
24 DuplicateTemplate { element: String, paths: Vec<String> },
26 TemplateReadError { path: String, message: String },
28}
29
30impl fmt::Display for RenderError {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self {
33 Self::UnclosedBinding { expr, context } => write!(
34 f,
35 "unclosed binding '{{{{{}': \
36 no closing '}}}}' found to end the expression \
37 — template: \"{context}\"",
38 expr
39 ),
40 Self::UnclosedUnescapedBinding { expr, context } => write!(
41 f,
42 "unclosed unescaped binding '{{{{{{{expr}': \
43 no closing '}}}}}}' found to end the expression \
44 — template: \"{context}\""
45 ),
46 Self::EmptyBinding { context } => write!(
47 f,
48 "empty binding '{{{{}}}}': \
49 the expression between '{{{{' and '}}}}' cannot be empty \
50 — template: \"{context}\""
51 ),
52 Self::MissingState { binding, context } => write!(
53 f,
54 "missing state: '{{{{{}}}}}' has no matching key in the provided state \
55 — template: \"{context}\"",
56 binding
57 ),
58 Self::UnclosedDirective { tag, context } => write!(
59 f,
60 "unclosed directive '<{tag}>': \
61 no matching '</{tag}>' closing tag was found \
62 — template: \"{context}\""
63 ),
64 Self::MissingValueAttribute { tag, context } => write!(
65 f,
66 "directive '<{tag}>' is missing a valid 'value=\"{{{{…}}}}\"' attribute \
67 — template: \"{context}\""
68 ),
69 Self::InvalidRepeatExpression { expr, context } => write!(
70 f,
71 "invalid repeat expression '{{{{{}}}}}': \
72 expected 'item in list' format \
73 — template: \"{context}\"",
74 expr
75 ),
76 Self::NotAnArray { binding, context } => write!(
77 f,
78 "type error: '{{{{{}}}}}' must resolve to a JSON array for use in <f-repeat> \
79 — template: \"{context}\"",
80 binding
81 ),
82 Self::JsonParse { message } => write!(
83 f,
84 "failed to parse state JSON: {message}"
85 ),
86 Self::DuplicateTemplate { element, paths } => write!(
87 f,
88 "duplicate template: element '<{element}>' is defined in multiple files: {}",
89 paths.join(", ")
90 ),
91 Self::TemplateReadError { path, message } => write!(
92 f,
93 "template read error: could not read '{path}': {message}"
94 ),
95 }
96 }
97}
98
99impl std::error::Error for RenderError {}
100
101pub fn template_context(template: &str, at: usize) -> String {
104 const PRE: usize = 20;
105 const POST: usize = 60;
106
107 let mut start = at.saturating_sub(PRE);
109 while start > 0 && !template.is_char_boundary(start) {
110 start -= 1;
111 }
112 let mut end = (at + POST).min(template.len());
114 while end < template.len() && !template.is_char_boundary(end) {
115 end += 1;
116 }
117
118 let prefix = if start > 0 { "…" } else { "" };
119 let suffix = if end < template.len() { "…" } else { "" };
120 format!("{prefix}{}{suffix}", &template[start..end])
121}
122
123pub fn truncate(s: &str, max: usize) -> String {
125 let mut chars = s.chars();
126 let mut out: String = chars.by_ref().take(max).collect();
127 if chars.next().is_some() {
128 out.push('…');
129 }
130 out
131}