use crate::{
error::{ExportError, TreeError},
link::Link,
node::Node,
tree::Tree,
};
use std::{fs::File, io::Write, path::Path};
use yaml_rust::{yaml, Yaml, YamlEmitter};
pub fn export<P>(tree: &Tree, path: P) -> Result<(), ExportError>
where
P: AsRef<Path>,
{
let source = tree_to_source(tree)?;
let mut file = File::create(path)?;
file.write_all(source.as_bytes())?;
Ok(())
}
pub fn tree_to_source(tree: &Tree) -> Result<String, ExportError> {
let yaml = tree_to_yaml(&tree)?;
let mut writer = String::new();
let mut emitter = YamlEmitter::new(&mut writer);
emitter.compact(true);
emitter.dump(&yaml)?;
Ok(writer)
}
fn tree_to_yaml(tree: &Tree) -> Result<Yaml, TreeError> {
let root_key = tree.root_key().ok_or_else(|| TreeError::RootNotSet())?;
if tree.nodes.len() == 0 {
return Err(TreeError::Validation("Node map has a length of 0".into()));
}
let mut node_map = yaml::Hash::new();
for (key, node) in &tree.nodes {
let yaml_key = Yaml::String(key.to_owned());
let yaml_node = node_to_yaml(&node)?;
node_map.insert(yaml_key, yaml_node);
}
let mut yaml = yaml::Hash::new();
yaml.insert(
Yaml::String("root".to_string()),
Yaml::String(root_key.to_owned()),
);
yaml.insert(Yaml::String("nodes".to_string()), Yaml::Hash(node_map));
Ok(Yaml::Hash(yaml))
}
fn node_to_yaml(node: &Node) -> Result<Yaml, TreeError> {
let mut map = yaml::Hash::new();
map.insert(
Yaml::String("dialogue".to_string()),
Yaml::String(node.dialogue.to_owned()),
);
if !node.links.is_empty() {
let mut links = yaml::Array::new();
for link in &node.links {
let yaml_link = link_to_yaml(link)?;
links.push(yaml_link);
}
map.insert(Yaml::String("links".to_string()), Yaml::Array(links));
}
let yaml = Yaml::Hash(map);
Ok(yaml)
}
fn link_to_yaml(link: &Link) -> Result<Yaml, TreeError> {
let mut map = yaml::Hash::new();
map.insert(
Yaml::String(link.to_key.to_owned()),
Yaml::String(link.dialogue.to_owned()),
);
Ok(Yaml::Hash(map))
}
#[cfg(test)]
#[test]
fn test_export() {
let mut tree = Tree::new();
let node = Node::new("start", "It's a bad day.");
tree.nodes.insert("start".to_owned(), node);
tree.set_root_key("start").unwrap();
assert!(export(&tree, "examples/dialogue_files/export.convo.yml").is_ok());
}
#[test]
fn test_export_path_exists() {
use crate::error::ExportError::IO;
let mut tree = Tree::new();
let node = Node::new("start", "It's a bad day.");
tree.nodes.insert("start".to_owned(), node);
tree.set_root_key("start").unwrap();
assert!(matches!(export(&tree, "/not/a/path").unwrap_err(), IO(_)));
}
#[test]
fn test_tree_to_source() {
let mut tree = Tree::new();
let node = Node::new("start", "It's a bad day.");
tree.nodes.insert("start".to_owned(), node);
tree.set_root_key("start").unwrap();
let source = r#"---
root: start
nodes:
start:
dialogue: "It's a bad day.""#;
assert_eq!(source, tree_to_source(&tree).unwrap());
}
#[test]
fn test_tree_to_source_root_exists() {
use crate::error::ExportError::Validation;
let mut tree = Tree::new();
let node = Node::new("start", "It's a bad day.");
tree.nodes.insert("start".to_owned(), node);
assert!(matches!(tree_to_source(&tree).unwrap_err(), Validation(_)));
}
#[test]
fn test_tree_to_source_nodes_exist() {
use crate::error::ExportError::Validation;
let mut tree = Tree::new();
unsafe { tree.set_root_key_unchecked("start") }
assert!(matches!(tree_to_source(&tree).unwrap_err(), Validation(_)));
}
#[test]
#[ignore = "Waiting on issue #3"]
fn test_tree_to_source_unreachable_nodes() {
use crate::error::ExportError::Validation;
let mut tree = Tree::new();
let node1 = Node::new("1", "It's a bad day.");
let node2 = Node::new("2", "It's a good day.");
tree.nodes.insert("1".to_owned(), node1);
tree.nodes.insert("2".to_owned(), node2);
tree.set_root_key("1").unwrap();
assert!(matches!(tree_to_source(&tree).unwrap_err(), Validation(_)));
let mut tree = Tree::new();
let mut parent = Node::new("parent", "I am the parent.");
let child = Node::new("child", "I am the child.");
Link::link(&mut parent, &child, "I make sure no orphan nodes exist.");
tree.nodes.insert("parent".to_owned(), parent);
tree.nodes.insert("child".to_owned(), child);
tree.set_root_key("child").unwrap();
assert!(matches!(tree_to_source(&tree).unwrap_err(), Validation(_)));
}
#[test]
#[ignore = "Waiting on issue #10"]
fn test_tree_to_source_invalid_links() {
use crate::error::ExportError::Validation;
let mut tree = Tree::new();
let mut node = Node::new("root", "I am the only node.");
let invalid_link = Link::new("invalid", "I am an invalid link");
node.links.push(invalid_link);
tree.nodes.insert("root".to_owned(), node);
tree.set_root_key("root").unwrap();
assert!(matches!(tree_to_source(&tree).unwrap_err(), Validation(_)));
}