pub mod filter;
pub mod mode;
pub mod profile;
pub use filter::filter_node;
pub use mode::LoadMode;
pub use profile::LoadError;
use crate::model::file::AgmFile;
#[must_use]
pub fn load(file: &AgmFile, mode: LoadMode) -> AgmFile {
let nodes = file.nodes.iter().map(|n| filter_node(n, mode)).collect();
AgmFile {
header: file.header.clone(),
nodes,
}
}
#[must_use]
pub fn load_nodes(file: &AgmFile, mode: LoadMode, node_ids: &[&str]) -> AgmFile {
let id_set: std::collections::HashSet<&str> = node_ids.iter().copied().collect();
let nodes = file
.nodes
.iter()
.filter(|n| id_set.contains(n.id.as_str()))
.map(|n| filter_node(n, mode))
.collect();
AgmFile {
header: file.header.clone(),
nodes,
}
}
pub fn load_profile(file: &AgmFile, profile_name: Option<&str>) -> Result<AgmFile, LoadError> {
profile::resolve_and_apply(file, profile_name)
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use crate::model::fields::{NodeType, Priority, Span};
use crate::model::file::{AgmFile, Header};
use crate::model::node::Node;
use super::*;
fn minimal_header() -> Header {
Header {
agm: "1.0".to_owned(),
package: "test.pkg".to_owned(),
version: "0.1.0".to_owned(),
title: None,
owner: None,
imports: None,
default_load: None,
description: None,
tags: None,
status: None,
load_profiles: None,
target_runtime: None,
}
}
fn make_node(id: &str) -> Node {
Node {
id: id.to_owned(),
node_type: NodeType::Facts,
summary: format!("node {id}"),
priority: Some(Priority::High),
stability: None,
confidence: None,
status: None,
depends: None,
related_to: None,
replaces: None,
conflicts: None,
see_also: None,
items: Some(vec!["item1".to_owned()]),
steps: None,
fields: None,
input: None,
output: None,
detail: Some("full detail".to_owned()),
rationale: None,
tradeoffs: None,
resolution: None,
examples: None,
notes: None,
code: None,
code_blocks: None,
verify: None,
agent_context: None,
target: None,
execution_status: None,
executed_by: None,
executed_at: None,
execution_log: None,
retry_count: None,
parallel_groups: None,
memory: None,
scope: None,
applies_when: None,
valid_from: None,
valid_until: None,
tags: None,
aliases: None,
keywords: None,
extra_fields: BTreeMap::new(),
span: Span::new(1, 5),
}
}
fn make_file(nodes: Vec<Node>) -> AgmFile {
AgmFile {
header: minimal_header(),
nodes,
}
}
#[test]
fn test_load_returns_all_nodes_filtered() {
let file = make_file(vec![make_node("a"), make_node("b")]);
let result = load(&file, LoadMode::Summary);
assert_eq!(result.nodes.len(), 2);
assert!(result.nodes[0].detail.is_none());
assert!(result.nodes[1].detail.is_none());
assert!(result.nodes[0].priority.is_some());
}
#[test]
fn test_load_nodes_returns_only_requested_ids() {
let file = make_file(vec![make_node("a"), make_node("b"), make_node("c")]);
let result = load_nodes(&file, LoadMode::Summary, &["a", "c"]);
assert_eq!(result.nodes.len(), 2);
let ids: Vec<&str> = result.nodes.iter().map(|n| n.id.as_str()).collect();
assert!(ids.contains(&"a"));
assert!(ids.contains(&"c"));
assert!(!ids.contains(&"b"));
}
#[test]
fn test_load_nodes_preserves_file_order() {
let file = make_file(vec![make_node("x"), make_node("y"), make_node("z")]);
let result = load_nodes(&file, LoadMode::Summary, &["z", "x"]);
assert_eq!(result.nodes[0].id, "x");
assert_eq!(result.nodes[1].id, "z");
}
#[test]
fn test_load_nodes_ignores_unknown_ids() {
let file = make_file(vec![make_node("a")]);
let result = load_nodes(&file, LoadMode::Summary, &["a", "nonexistent"]);
assert_eq!(result.nodes.len(), 1);
assert_eq!(result.nodes[0].id, "a");
}
}