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            canonical_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        }
76    }
77
78    #[test]
79    fn signature_text_formats_types() {
80        let stage = test_stage();
81        assert_eq!(signature_text(&stage), "Text -> Number");
82    }
83
84    #[test]
85    fn description_text_returns_description() {
86        let stage = test_stage();
87        assert_eq!(description_text(&stage), "Convert text to number");
88    }
89
90    #[test]
91    fn description_text_appends_aliases_and_tags() {
92        let mut stage = test_stage();
93        stage.aliases = vec!["parse_float".into(), "atof".into()];
94        stage.tags = vec!["scalar".into(), "pure".into()];
95        let text = description_text(&stage);
96        assert!(text.starts_with("Convert text to number"));
97        assert!(text.contains("Aliases: parse_float, atof"));
98        assert!(text.contains("Tags: scalar, pure"));
99    }
100
101    #[test]
102    fn examples_text_formats_pairs() {
103        let stage = test_stage();
104        let text = examples_text(&stage);
105        assert!(text.contains("\"42\" => 42"));
106        assert!(text.contains("\"3\" => 3"));
107    }
108}