Skip to main content

brooks_lib/dcdn/processors/
mel.rs

1// brooks, Copyright 2026, Will Hawkins
2//
3// This file is part of brooks.
4
5// This file is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <https://www.gnu.org/licenses/>.
17
18//! The Mel Generic Metadata Processing Implementation
19
20use crate::{
21    cdni::mel::spec::TypedSetVariable,
22    dcdn::{
23        imdi,
24        interpret::{
25            Error::{self, InvalidType},
26            MetadataInformationResultElements, Stages,
27        },
28        processors::SimpleProcessorsInterpreterContext,
29    },
30    logging::{LogLevel::Debug, LogMsgs},
31    mel::{
32        self, MEL_USER_SET_VARIABLE_STRUCT_NAME,
33        interpreter::{
34            interpret::MelInterpContext,
35            values::{TypedValue, Value},
36        },
37        types::{Struct, Type},
38    },
39    processing::interpret::{InterpretationResult, Interpreter},
40    verifying::mel::verify::SetVariableVerificationKey,
41};
42
43impl
44    Interpreter<
45        SimpleProcessorsInterpreterContext<'_>,
46        (imdi::ResultValue, MetadataInformationResultElements),
47        Stages,
48        Error,
49    > for TypedSetVariable<SetVariableVerificationKey>
50{
51    fn interpret<'a>(
52        &self,
53        input: SimpleProcessorsInterpreterContext<'a>,
54    ) -> InterpretationResult<
55        SimpleProcessorsInterpreterContext<'a>,
56        (imdi::ResultValue, MetadataInformationResultElements),
57        Error,
58    > {
59        let lmsgs = LogMsgs::new(Debug);
60
61        // First, try to get the var struct from the scope.
62        let var = input
63            .scopes
64            .lookup(MEL_USER_SET_VARIABLE_STRUCT_NAME)
65            .ok_or(Error::RuntimeError("var not in scope".into()))?;
66
67        let (mut var, var_tpe) = match var {
68            TypedValue {
69                value: Value::Struct(value),
70                tpe: Type::Struct(tpe),
71            } => (value, tpe),
72            TypedValue { value: _, tpe } => {
73                return Err(InvalidType(
74                    Type::Struct(Struct::new(MEL_USER_SET_VARIABLE_STRUCT_NAME)).to_string(),
75                    tpe.to_string(),
76                )
77                .into());
78            }
79        };
80
81        if let Some(expression) = &self.value.aug.expression {
82            let result = mel::interpreter::interpret(
83                expression,
84                MelInterpContext {
85                    val: None,
86                    scopes: input.scopes.clone(),
87                    log: lmsgs,
88                },
89            )
90            .map_err(|e| Error::RuntimeError(Box::new(e)))?;
91
92            var.insert_field(
93                &self.value.variable_name,
94                result.val.ok_or(Error::RuntimeError(
95                    "Missing value after successful interpretation of Mel expression".into(),
96                ))?,
97            )
98            .map_err(|e| Error::RuntimeError(e))?;
99
100            let output = input.with_new_value_in_scope(
101                MEL_USER_SET_VARIABLE_STRUCT_NAME,
102                TypedValue {
103                    value: Value::Struct(var),
104                    tpe: Type::Struct(var_tpe),
105                },
106            );
107
108            Ok((output, (None, MetadataInformationResultElements::default())))
109        } else if let Some(_value) = &self.value.aug.value {
110            todo!()
111        } else {
112            todo!()
113        }
114    }
115
116    fn at(&self, stage: Stages) -> bool {
117        stage == Stages::Pre
118    }
119}
120
121#[cfg(test)]
122mod test {
123    use std::{assert_matches, str::FromStr};
124
125    use url::Url;
126
127    use crate::{
128        cdni::{
129            mel::spec::TypedSetVariable,
130            tests::test_helpers::{generic_metadata, set_variable},
131        },
132        dcdn::{
133            imdi, interpret::ProcessedRequestResponse,
134            processors::SimpleProcessorsInterpreterContext,
135        },
136        environment::scope::Scopes,
137        mel::{
138            MEL_USER_SET_VARIABLE_STRUCT_NAME,
139            interpreter::values::{TypedValue, Value, mel_var_value_from_type},
140            types::{Type, builtin_function_types, minimal_core_variable_types},
141        },
142        processing::interpret::Interpreter,
143        verifying::{
144            mel::verify::SetVariableMetadataAnalyzer,
145            verify::{CdniVerificationKey, MetadataAnalysisContext, Verifier},
146        },
147    };
148
149    #[test]
150    fn verify_test() {
151        let variable = set_variable("is_bad".to_string(), "true".to_string(), Some(true));
152
153        let json = generic_metadata(
154            &TypedSetVariable::<()>::typed_cdni_metadata_name(),
155            Some(
156                serde_json::to_value(variable.value)
157                    .expect("Could not convert valid SetVariable to Value"),
158            ),
159        );
160
161        let svma = SetVariableMetadataAnalyzer {};
162
163        let result = svma
164            .analyze(
165                &json,
166                MetadataAnalysisContext {
167                    scopes: (&builtin_function_types() + &minimal_core_variable_types()).into(),
168                },
169            )
170            .expect("Could not analyze correct MI.SetVariable");
171
172        let svmi = match result.1 {
173            CdniVerificationKey::SetVariable(typed_set_variable) => typed_set_variable,
174            _ => panic!("Result of verifying correct MI.SetVariable was of incorrect type"),
175        };
176
177        let var = result
178            .0
179            .scopes
180            .lookup(MEL_USER_SET_VARIABLE_STRUCT_NAME)
181            .expect("Could not get the var struct from the types of the analyzed generic metadata");
182
183        let var = match var {
184            Type::Struct(s) => s,
185            _ => panic!("var had the wrong type in the analyzed generic metadata"),
186        };
187
188        let rt = tokio::runtime::Builder::new_current_thread()
189            .build()
190            .expect("Could not get a runtime to use for testing interpret function");
191        let result = svmi
192            .interpret(SimpleProcessorsInterpreterContext {
193                scopes: Scopes::<TypedValue>::default().insert(
194                    MEL_USER_SET_VARIABLE_STRUCT_NAME,
195                    mel_var_value_from_type(var),
196                ),
197                mode: imdi::InterpretMode::Request,
198                runtime: rt.handle(),
199                req: Box::new(ProcessedRequestResponse::new_request(
200                    &Url::from_str("http://www.example.com/")
201                        .expect("Could not get a URI from a valid HTTP address"),
202                    &http::Method::GET,
203                )),
204                res: None,
205            })
206            .expect("Could not interpret correct MI.SetVariable");
207
208        let value = match result
209            .0
210            .scopes
211            .lookup(MEL_USER_SET_VARIABLE_STRUCT_NAME)
212            .unwrap_or_else(|| {
213                panic!(
214                    "Could not get {} from resulting scope",
215                    MEL_USER_SET_VARIABLE_STRUCT_NAME
216                )
217            }) {
218            TypedValue {
219                value: Value::Struct(s),
220                tpe: Type::Struct(_),
221            } => s.fields.get("is_bad").cloned(),
222            _ => panic!(
223                "{} is not a struct in resulting scope",
224                MEL_USER_SET_VARIABLE_STRUCT_NAME
225            ),
226        };
227
228        let value = match value {
229            Some(v) => v,
230            None => panic!(
231                "is_bad field of {} does not have a value in resulting scope",
232                MEL_USER_SET_VARIABLE_STRUCT_NAME
233            ),
234        };
235        assert_matches!(value, TypedValue{ value: Value::Boolean(v), tpe: _} if v)
236    }
237}