Skip to main content

microcad_lang/parse/
format_string.rs

1// Copyright © 2025-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use crate::{parse::*, parser::*, syntax::*};
5use microcad_lang_base::Refer;
6use microcad_syntax::ast;
7
8impl FromAst for FormatExpression {
9    type AstNode = ast::StringExpression;
10
11    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
12        Ok(FormatExpression::new(
13            node.specification
14                .is_some()
15                .then(|| FormatSpec::from_ast(&node.specification, context))
16                .transpose()?,
17            Expression::from_ast(&node.expression, context)?,
18            context.src_ref(&node.span),
19        ))
20    }
21}
22
23impl FromAst for FormatSpec {
24    type AstNode = ast::StringFormatSpecification;
25
26    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
27        fn transpose_ref<T: Clone, E: Clone>(opt: &Option<Result<T, E>>) -> Result<Option<&T>, E> {
28            match opt.as_ref() {
29                None => Ok(None),
30                Some(Err(e)) => Err(e.clone()),
31                Some(Ok(t)) => Ok(Some(t)),
32            }
33        }
34        Ok(FormatSpec {
35            width: transpose_ref(&node.width)
36                .map_err(|(e, span)| {
37                    ParseError::ParseIntError(Refer::new(e, context.src_ref(&span)))
38                })?
39                .copied(),
40            precision: transpose_ref(&node.precision)
41                .map_err(|(e, span)| {
42                    ParseError::ParseIntError(Refer::new(e, context.src_ref(&span)))
43                })?
44                .copied(),
45            src_ref: context.src_ref(&node.span),
46        })
47    }
48}
49
50impl FromAst for FormatString {
51    type AstNode = ast::FormatString;
52
53    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
54        let parts = node
55            .parts
56            .iter()
57            .map(|part| FormatStringInner::from_ast(part, context))
58            .collect::<Result<Vec<_>, _>>()?;
59        Ok(FormatString(Refer::new(parts, context.src_ref(&node.span))))
60    }
61}
62
63impl FromAst for FormatStringInner {
64    type AstNode = ast::StringPart;
65
66    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError> {
67        Ok(match node {
68            ast::StringPart::Char(c) => {
69                FormatStringInner::String(Refer::new(c.character.into(), context.src_ref(&c.span)))
70            }
71            ast::StringPart::Content(s) => {
72                FormatStringInner::String(Refer::new(s.content.clone(), context.src_ref(&s.span)))
73            }
74            ast::StringPart::Expression(e) => {
75                FormatStringInner::FormatExpression(FormatExpression::from_ast(e, context)?)
76            }
77        })
78    }
79}