Skip to main content

noether_engine/index/
text.rs

1use noether_core::stage::Stage;
2
3/// Generate text for the signature index: "input_type -> output_type".
4pub fn signature_text(stage: &Stage) -> String {
5    format!("{} -> {}", stage.signature.input, stage.signature.output)
6}
7
8/// Generate text for the semantic/description index.
9/// Aliases and tags are appended so that vocabulary-mismatched queries still
10/// match (e.g. "strlen" → `text_length`, "network" → all IO stages).
11pub fn description_text(stage: &Stage) -> String {
12    let mut parts = vec![stage.description.clone()];
13    if !stage.aliases.is_empty() {
14        parts.push(format!("Aliases: {}", stage.aliases.join(", ")));
15    }
16    if !stage.tags.is_empty() {
17        parts.push(format!("Tags: {}", stage.tags.join(", ")));
18    }
19    parts.join("\n")
20}
21
22/// Generate text for the example index: all input/output pairs.
23pub fn examples_text(stage: &Stage) -> String {
24    stage
25        .examples
26        .iter()
27        .map(|ex| format!("{} => {}", ex.input, ex.output))
28        .collect::<Vec<_>>()
29        .join("\n")
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35    use noether_core::effects::EffectSet;
36    use noether_core::stage::{CostEstimate, Example, StageId, StageLifecycle, StageSignature};
37    use noether_core::types::NType;
38    use std::collections::BTreeSet;
39
40    fn test_stage() -> Stage {
41        Stage {
42            id: StageId("test".into()),
43            signature_id: None,
44            signature: StageSignature {
45                input: NType::Text,
46                output: NType::Number,
47                effects: EffectSet::pure(),
48                implementation_hash: "impl".into(),
49            },
50            capabilities: BTreeSet::new(),
51            cost: CostEstimate {
52                time_ms_p50: None,
53                tokens_est: None,
54                memory_mb: None,
55            },
56            description: "Convert text to number".into(),
57            examples: vec![
58                Example {
59                    input: serde_json::json!("42"),
60                    output: serde_json::json!(42),
61                },
62                Example {
63                    input: serde_json::json!("3"),
64                    output: serde_json::json!(3),
65                },
66            ],
67            lifecycle: StageLifecycle::Active,
68            ed25519_signature: None,
69            signer_public_key: None,
70            implementation_code: None,
71            implementation_language: None,
72            ui_style: None,
73            tags: vec![],
74            aliases: vec![],
75            name: None,
76            properties: Vec::new(),
77        }
78    }
79
80    #[test]
81    fn signature_text_formats_types() {
82        let stage = test_stage();
83        assert_eq!(signature_text(&stage), "Text -> Number");
84    }
85
86    #[test]
87    fn description_text_returns_description() {
88        let stage = test_stage();
89        assert_eq!(description_text(&stage), "Convert text to number");
90    }
91
92    #[test]
93    fn description_text_appends_aliases_and_tags() {
94        let mut stage = test_stage();
95        stage.aliases = vec!["parse_float".into(), "atof".into()];
96        stage.tags = vec!["scalar".into(), "pure".into()];
97        let text = description_text(&stage);
98        assert!(text.starts_with("Convert text to number"));
99        assert!(text.contains("Aliases: parse_float, atof"));
100        assert!(text.contains("Tags: scalar, pure"));
101    }
102
103    #[test]
104    fn examples_text_formats_pairs() {
105        let stage = test_stage();
106        let text = examples_text(&stage);
107        assert!(text.contains("\"42\" => 42"));
108        assert!(text.contains("\"3\" => 3"));
109    }
110}