1use anyhow::{bail, Result};
2use std::{
3 fs::{self, create_dir_all},
4 path::{Path, PathBuf},
5};
6use yaml_rust::{yaml::Hash, Yaml, YamlEmitter, YamlLoader};
7
8pub fn unpack_from_file(source: PathBuf, dest: PathBuf) -> Result<()> {
10 let orb = get_orb_from_path(source)?;
11 let orb_root = unpack_to_dir(orb, &dest)?;
12 write_root_file(orb_root, dest)
13}
14
15fn unpack_to_dir(mut orb: Hash, dest: &Path) -> Result<Hash> {
18 for section_name in ["commands", "jobs", "executors", "examples"] {
20 if let Some(Yaml::Hash(section)) = orb.remove(&Yaml::from_str(section_name)) {
21 extract_to_subdirectory(section, &dest.join(section_name))?;
22 }
23 }
24 Ok(orb)
25}
26
27fn extract_to_subdirectory(section: Hash, dest: &PathBuf) -> Result<()> {
28 create_dir_all(&dest)?;
30
31 for (key, value) in section.iter() {
32 if let Yaml::String(key) = key {
33 let file_name = format!("{key}.yml");
34 let file_path = dest.join(file_name);
35 write_yaml_to_file(file_path, value)?;
36 } else {
37 bail!("all keys must be strings")
38 }
39 }
40 Ok(())
41}
42
43fn write_root_file(root: Hash, dest: PathBuf) -> Result<()> {
44 let root_path = dest.join("@orb.yml");
45 write_yaml_to_file(root_path, &Yaml::Hash(root))
46}
47
48fn write_yaml_to_file(path: PathBuf, value: &Yaml) -> Result<()> {
49 let mut output = String::new();
50 let mut emitter = YamlEmitter::new(&mut output);
51 emitter.dump(value)?;
52 fs::write(path, &output[4..])?; Ok(())
54}
55
56fn get_orb_from_path(path: PathBuf) -> Result<Hash> {
57 let file_contents = fs::read_to_string(path)?;
58 let yaml_documents = YamlLoader::load_from_str(&file_contents)?;
59
60 match &yaml_documents[..] {
61 [Yaml::Hash(orb)] => Ok(orb.clone()),
62 _ => bail!(
63 "expected 1 document in file, but found {}",
64 yaml_documents.len()
65 ),
66 }
67}