Skip to main content

cdoc_parser/code_ast/
types.rs

1//! Types for exercise definitions.
2
3use linked_hash_map::LinkedHashMap;
4use serde::{Deserialize, Serialize};
5
6use cowstr::CowStr;
7use std::io::{BufWriter, Write};
8
9pub trait Output {
10    fn write_string(&self, solution: bool) -> String;
11}
12
13/// Represents a line of source code. Can either be markup (descriptions of the exercise) or
14/// code (regular source code).
15#[derive(Clone, Debug, Serialize, Deserialize)]
16pub enum Content {
17    Markup(CowStr),
18    Src(CowStr),
19}
20
21/// An exercise element with a placeholder and a solution
22#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
23pub struct Solution {
24    pub placeholder: Option<CowStr>,
25    pub solution: CowStr,
26}
27
28/// Top-level structure. A code file is split into these types.
29#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
30#[serde(rename = "value", untagged)]
31pub enum CodeElem {
32    Solution(Solution),
33    Src(String),
34}
35
36#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
37pub enum CodeContent {
38    Plain(CowStr),
39    Parsed {
40        blocks: Vec<CodeElem>,
41        meta: LinkedHashMap<CowStr, CowStr>,
42        hash: u64,
43    },
44}
45
46impl CodeContent {
47    pub fn to_string(&self, with_solution: bool) -> anyhow::Result<String> {
48        let mut buf = BufWriter::new(Vec::new());
49        match &self {
50            CodeContent::Plain(s) => Ok(s.to_string()),
51            CodeContent::Parsed { blocks, .. } => {
52                for block in blocks {
53                    match block {
54                        CodeElem::Solution(s) => {
55                            if with_solution {
56                                buf.write_all(s.solution.as_bytes())?;
57                            } else {
58                                s.placeholder
59                                    .as_ref()
60                                    .map(|p| buf.write(p.as_bytes()))
61                                    .transpose()?;
62                            }
63                        }
64                        CodeElem::Src(s) => {
65                            buf.write_all(s.as_bytes())?;
66                        }
67                    }
68                }
69
70                Ok(String::from_utf8(buf.into_inner()?)?)
71            }
72        }
73    }
74}