Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "yaml-include"
version = "0.7.1"
version = "0.8.0"
edition = "2021"
authors = ["Merleur l'enchantin <le.neko@gmail.com>"]
license = "GPL-3.0"
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ cargo install yaml-include
- include and parse recursively `yaml` (and `json`) files
- include `markdown` and `txt` text files
- include other types as `base64` encoded binary data.
- hint or force extension with `!include { "path": "<file_path>", "extension": "txt"}`
- by default handle gracefully circular references with `!circular` tag

## Usage
Expand Down Expand Up @@ -57,6 +58,7 @@ turns this:
data:
- !include file_a.yml
- !include file_b.yml
- !include { "path": "file_a.yml", "extension": "txt"}
```

`file_a.yml`:
Expand Down
4 changes: 4 additions & 0 deletions data/expected.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ Nested:
- people
- need to be recursive: yes
whatever: CECI est un texte
- |
Bye:
- people
- !include c.yml
- Bye:
- people
- need to be recursive: yes
Expand Down
4 changes: 2 additions & 2 deletions data/sample/file_a.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
something:
- this
- that
- this
- that
4 changes: 2 additions & 2 deletions data/sample/file_b.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
other:
- text: !include file_c.txt
- markdown: !include file_d.md
- text: !include file_c.txt
- markdown: !include file_d.md
2 changes: 1 addition & 1 deletion data/sample/file_c.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
This is some long multiline
text i don't want to edit
inline in a long yaml file
inline in a long yaml file
2 changes: 1 addition & 1 deletion data/sample/file_e.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
3,
4
]
}
}
6 changes: 3 additions & 3 deletions data/sample/main.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
yaml:
- !include file_a.yml
- !include file_b.yml
- !include file_e.json
- !include file_a.yml
- !include file_b.yml
- !include file_e.json
1 change: 1 addition & 0 deletions data/simple/nested/a.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ World:
- Tralala
- 42
- !include b.yml
- !include { "path": "b.yml", "extension": "txt" }
2 changes: 1 addition & 1 deletion data/simple/other.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
"this": "or that",
"data": !include ../root.yml
}
}
}
168 changes: 117 additions & 51 deletions src/transformer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,79 @@ use std::{
fmt,
fs::{canonicalize, read_to_string},
path::PathBuf,
str::FromStr,
};

use crate::helpers::{load_as_base64, load_yaml};

struct FilePath {
path: PathBuf,
extension: Extension,
}

enum Extension {
Yaml,
Text,
Binary,
}

#[derive(Debug)]
enum ParseError {
MissingPath,
MissingExtension,
}

impl FromStr for Extension {
type Err = ();

fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"yaml" | "yml" | "json" => Ok(Self::Yaml),
"md" | "markdown" | "txt" => Ok(Self::Text),
_ => Ok(Self::Binary),
}
}
}

impl TryFrom<Mapping> for FilePath {
type Error = ParseError;

fn try_from(value: Mapping) -> Result<Self, Self::Error> {
let path = value
.get("path")
.and_then(|value| value.as_str())
.ok_or(ParseError::MissingPath)?
.into();

let extension = Extension::from_str(
value
.get("extension")
.and_then(|value| value.as_str())
.ok_or(ParseError::MissingExtension)?,
)
.expect("Infaillible conversion");

Ok(Self { path, extension })
}
}

impl TryFrom<String> for FilePath {
type Error = ParseError;

fn try_from(value: String) -> Result<Self, Self::Error> {
let path: PathBuf = value.into();

let extension = Extension::from_str(
path.extension()
.and_then(|ext| ext.to_str())
.ok_or(ParseError::MissingExtension)?,
)
.expect("Infaillible conversion");

Ok(Self { path, extension })
}
}

/// Processing yaml with include documents through `!include <path>` tag.
///
/// ## Features
Expand Down Expand Up @@ -116,8 +185,11 @@ impl Transformer {
)),
Value::Tagged(tagged_value) => match tagged_value.tag.to_string().as_str() {
"!include" => {
let value = tagged_value.value.as_str().unwrap();
let file_path = PathBuf::from(value);
let file_path: FilePath = match tagged_value.value {
Value::String(path) => path.try_into().unwrap(),
Value::Mapping(mapping) => mapping.try_into().unwrap(),
_ => panic!("Unsupported Value"),
};

self.handle_include_extension(file_path)
}
Expand All @@ -128,60 +200,54 @@ impl Transformer {
}
}

fn handle_include_extension(&self, file_path: PathBuf) -> Value {
let normalized_file_path = self.process_path(&file_path);

let result = match normalized_file_path.extension() {
Some(os_str) => match os_str.to_str() {
Some("yaml") | Some("yml") | Some("json") => {
match Transformer::new_node(
normalized_file_path,
self.error_on_circular,
Some(self.seen_paths.clone()),
) {
Ok(transformer) => transformer.parse(),
Err(e) => {
if self.error_on_circular {
// TODO: probably something better to do than panic ?
panic!("{:?}", e);
}
fn handle_include_extension(&self, file_path: FilePath) -> Value {
let normalized_file_path = self.process_path(&file_path.path);

return Value::Tagged(
TaggedValue {
tag: Tag::new("circular"),
value: Value::String(file_path.display().to_string()),
}
.into(),
);
let result = match file_path.extension {
Extension::Yaml => {
match Transformer::new_node(
normalized_file_path,
self.error_on_circular,
Some(self.seen_paths.clone()),
) {
Ok(transformer) => transformer.parse(),
Err(e) => {
if self.error_on_circular {
panic!("{:?}", e);
}

return Value::Tagged(
TaggedValue {
tag: Tag::new("circular"),
value: Value::String(file_path.path.display().to_string()),
}
.into(),
);
}
}
// inlining markdow and text files
Some("txt") | Some("markdown") | Some("md") => {
Value::String(read_to_string(normalized_file_path).unwrap())
}
// inlining other include as binary files
None | Some(&_) => Value::Tagged(Box::new(TaggedValue {
tag: Tag::new("binary"),
value: Value::Mapping(Mapping::from_iter([
(
Value::String("filename".into()),
Value::String(
normalized_file_path
.file_name()
.unwrap()
.to_string_lossy()
.to_string(),
),
),
(
Value::String("base64".into()),
Value::String(load_as_base64(&normalized_file_path).unwrap()),
}
// inlining markdow and text files
Extension::Text => Value::String(read_to_string(normalized_file_path).unwrap()),
// inlining other include as binary files
Extension::Binary => Value::Tagged(Box::new(TaggedValue {
tag: Tag::new("binary"),
value: Value::Mapping(Mapping::from_iter([
(
Value::String("filename".into()),
Value::String(
normalized_file_path
.file_name()
.unwrap()
.to_string_lossy()
.to_string(),
),
])),
})),
},
_ => panic!("{:?} path missing file extension", normalized_file_path),
),
(
Value::String("base64".into()),
Value::String(load_as_base64(&normalized_file_path).unwrap()),
),
])),
})),
};

result
Expand Down