Skip to main content

microcad_lang/parse/
format_string.rs

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