1use super::scope::Scope;
2use super::{ResolverError, WriteValue};
3
4use std::borrow::Borrow;
5use std::fmt;
6
7use fluent_syntax::ast;
8
9use crate::memoizer::MemoizerKind;
10use crate::resolver::ResolveValue;
11use crate::resource::FluentResource;
12use crate::types::FluentValue;
13
14const MAX_PLACEABLES: u8 = 100;
15
16impl<'p> WriteValue for ast::Pattern<&'p str> {
17 fn write<'scope, 'errors, W, R, M>(
18 &'scope self,
19 w: &mut W,
20 scope: &mut Scope<'scope, 'errors, R, M>,
21 ) -> fmt::Result
22 where
23 W: fmt::Write,
24 R: Borrow<FluentResource>,
25 M: MemoizerKind,
26 {
27 let len = self.elements.len();
28
29 for elem in &self.elements {
30 if scope.dirty {
31 return Ok(());
32 }
33
34 match elem {
35 ast::PatternElement::TextElement { value } => {
36 if let Some(ref transform) = scope.bundle.transform {
37 w.write_str(&transform(value))?;
38 } else {
39 w.write_str(value)?;
40 }
41 }
42 ast::PatternElement::Placeable { ref expression } => {
43 scope.placeables += 1;
44 if scope.placeables > MAX_PLACEABLES {
45 scope.dirty = true;
46 scope.add_error(ResolverError::TooManyPlaceables);
47 return Ok(());
48 }
49
50 let needs_isolation = scope.bundle.use_isolating
51 && len > 1
52 && !matches!(
53 expression,
54 ast::Expression::Inline(ast::InlineExpression::MessageReference { .. },)
55 | ast::Expression::Inline(
56 ast::InlineExpression::TermReference { .. },
57 )
58 | ast::Expression::Inline(
59 ast::InlineExpression::StringLiteral { .. },
60 )
61 );
62 if needs_isolation {
63 w.write_char('\u{2068}')?;
64 }
65 scope.maybe_track(w, self, expression)?;
66 if needs_isolation {
67 w.write_char('\u{2069}')?;
68 }
69 }
70 }
71 }
72 Ok(())
73 }
74
75 fn write_error<W>(&self, _w: &mut W) -> fmt::Result
76 where
77 W: fmt::Write,
78 {
79 unreachable!()
80 }
81}
82
83impl<'p> ResolveValue for ast::Pattern<&'p str> {
84 fn resolve<'source, 'errors, R, M>(
85 &'source self,
86 scope: &mut Scope<'source, 'errors, R, M>,
87 ) -> FluentValue<'source>
88 where
89 R: Borrow<FluentResource>,
90 M: MemoizerKind,
91 {
92 let len = self.elements.len();
93
94 if len == 1 {
95 if let ast::PatternElement::TextElement { value } = self.elements[0] {
96 return scope
97 .bundle
98 .transform
99 .map_or_else(|| value.into(), |transform| transform(value).into());
100 }
101 }
102
103 let mut result = String::new();
104 self.write(&mut result, scope)
105 .expect("Failed to write to a string.");
106 result.into()
107 }
108}
109