Skip to content

Commit

Permalink
Move all YAML declarations under the file_format::yaml module.
Browse files Browse the repository at this point in the history
  • Loading branch information
gibbz00 committed Dec 16, 2023
1 parent 662add5 commit e998d39
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 287 deletions.
53 changes: 0 additions & 53 deletions crates/lib/src/rops_file/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,57 +26,4 @@ mod mock {
}
}
}

#[cfg(feature = "yaml")]
mod yaml {
use super::*;

impl<S: RopsFileState> MockFileFormatUtil<YamlFileFormat> for RopsFile<S, YamlFileFormat>
where
RopsFileMap<S, YamlFileFormat>: MockFileFormatUtil<YamlFileFormat>,
{
fn mock_format_display() -> String {
indoc::formatdoc! {"
{}
sops:
{}",
RopsFileMap::mock_format_display(),
textwrap::indent(&RopsFileMetadata::mock_format_display()," ")
}
}
}
}
}

#[cfg(test)]
mod tests {
#[cfg(feature = "yaml")]
mod yaml {
use crate::*;

#[test]
fn serializes_decrypted_rops_file() {
FileFormatTestUtils::assert_serialization::<YamlFileFormat, RopsFile<Decrypted, YamlFileFormat>>()
}

#[test]
fn deserializes_decrypted_rops_file() {
FileFormatTestUtils::assert_deserialization::<YamlFileFormat, RopsFile<Decrypted, YamlFileFormat>>()
}

#[cfg(feature = "aes-gcm")]
mod aes_gcm {
use super::*;

#[test]
fn serializes_encrypted_rops_file() {
FileFormatTestUtils::assert_serialization::<YamlFileFormat, RopsFile<Encrypted<AES256GCM>, YamlFileFormat>>()
}

#[test]
fn deserializes_encrypted_rops_file() {
FileFormatTestUtils::assert_deserialization::<YamlFileFormat, RopsFile<Encrypted<AES256GCM>, YamlFileFormat>>()
}
}
}
}
109 changes: 0 additions & 109 deletions crates/lib/src/rops_file/format/yaml.rs

This file was deleted.

86 changes: 86 additions & 0 deletions crates/lib/src/rops_file/format/yaml/decrypted_map_to_tree.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use indexmap::IndexMap;
use serde_yaml::{Mapping as YamlMap, Value as YamlValue};

use crate::*;

impl TryFrom<RopsFileMap<Decrypted, YamlFileFormat>> for RopsTree<Decrypted> {
type Error = DecryptedMapToTreeError;

fn try_from(rops_file_map: RopsFileMap<Decrypted, YamlFileFormat>) -> Result<Self, Self::Error> {
return recursive_map_call(rops_file_map.into_inner_map());

fn recursive_map_call(yaml_map: YamlMap) -> Result<RopsTree<Decrypted>, DecryptedMapToTreeError> {
let mut inner_map = IndexMap::default();

for (yaml_key, value_yaml) in yaml_map {
inner_map.insert(validate_key(yaml_key)?, recursive_value_call(value_yaml)?);
}

return Ok(RopsTree::Map(inner_map));

fn validate_key(yaml_value: YamlValue) -> Result<String, DecryptedMapToTreeError> {
match yaml_value {
YamlValue::String(string) => Ok(string),
other => Err(DecryptedMapToTreeError::NonStringKey(
serde_yaml::to_string(&other).expect("yaml value not serializable"),
)),
}
}
}

fn recursive_value_call(yaml_value: YamlValue) -> Result<RopsTree<Decrypted>, DecryptedMapToTreeError> {
Ok(match yaml_value {
// SOPS simply throws away tags, so do we for now.
// It can, however, deserialize manually added tags to encrypted documents,
// so we could in theory keep the tags somewhere without breaking SOPS compatability.
YamlValue::Tagged(tagged) => recursive_value_call(tagged.value)?,
YamlValue::Mapping(map) => recursive_map_call(map)?,
YamlValue::Bool(boolean) => RopsTree::Leaf(RopsValue::Boolean(boolean)),
YamlValue::String(string) => RopsTree::Leaf(RopsValue::String(string)),
YamlValue::Number(number) => RopsTree::Leaf(match number.is_f64() {
true => RopsValue::Float(number.as_f64().expect("number not a f64")),
false => RopsValue::Integer(
number
.as_i64()
.ok_or_else(|| DecryptedMapToTreeError::IntegerOutOfRange(number.as_u64().expect("number not an u64")))?,
),
}),
YamlValue::Sequence(sequence) => {
RopsTree::Sequence(sequence.into_iter().map(recursive_value_call).collect::<Result<Vec<_>, _>>()?)
}
YamlValue::Null => RopsTree::Null,
})
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn transforms_decrypted_yaml_map() {
assert_eq!(
RopsTree::mock(),
RopsFileMap::<Decrypted, YamlFileFormat>::mock().try_into().unwrap()
)
}

#[test]
fn dissallows_non_string_keys() {
let file_map = RopsFileMap::from_inner_map(serde_yaml::from_str::<YamlMap>("123: 456").unwrap());
assert!(matches!(
RopsTree::try_from(file_map).unwrap_err(),
DecryptedMapToTreeError::NonStringKey(_)
))
}

#[test]
fn dissallows_out_of_range_integers() {
let file_map = RopsFileMap::from_inner_map(serde_yaml::from_str::<YamlMap>(&format!("invalid_integer: {}", u64::MAX)).unwrap());
assert!(matches!(
RopsTree::try_from(file_map).unwrap_err(),
DecryptedMapToTreeError::IntegerOutOfRange(_)
))
}
}
112 changes: 112 additions & 0 deletions crates/lib/src/rops_file/format/yaml/mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
mod rops_file {
use crate::*;

impl<S: RopsFileState> MockFileFormatUtil<YamlFileFormat> for RopsFile<S, YamlFileFormat>
where
RopsFileMap<S, YamlFileFormat>: MockFileFormatUtil<YamlFileFormat>,
{
fn mock_format_display() -> String {
indoc::formatdoc! {"
{}
sops:
{}",
RopsFileMap::mock_format_display(),
textwrap::indent(&RopsFileMetadata::mock_format_display()," ")
}
}
}
}

mod map {
use crate::*;

impl MockFileFormatUtil<YamlFileFormat> for RopsFileMap<Decrypted, YamlFileFormat> {
fn mock_format_display() -> String {
indoc::indoc! {"
hello: world!
nested_map:
null_key: null
array:
- string
- nested_map_in_array:
integer: 1234
- float: 1234.56789
booleans:
- true
- false"
}
.to_string()
}
}

#[cfg(feature = "aes-gcm")]
impl MockFileFormatUtil<YamlFileFormat> for RopsFileMap<Encrypted<AES256GCM>, YamlFileFormat> {
fn mock_format_display() -> String {
indoc::indoc! {"
hello: ENC[AES256_GCM,data:3S1E9am/,iv:WUQoQTrRXw/tUgwpmSG69xWtd5dVMfe8qUly1VB8ucM=,tag:nQUDkuh0OR1cjR5hGC5jOw==,type:str]
nested_map:
null_key: null
array:
- ENC[AES256_GCM,data:ANbeNrGp,iv:PRWGCPdOttPr5dlzT9te7WWCZ90J7+CvfY1vp60aADM=,tag:PvSLx4pLT5zRKOU0df8Xlg==,type:str]
- nested_map_in_array:
integer: ENC[AES256_GCM,data:qTW5qw==,iv:ugMxvR8YPwDgn2MbBpDX0lpCqzJY3GerhbA5jEKUbwE=,tag:d8utfA76C4XPzJyDfgE4Pw==,type:int]
- float: ENC[AES256_GCM,data:/MTg0fCennyN8g==,iv:+/8+Ljm+cls7BbDYZnlg6NVFkrkw4GkEfWU2aGW57qE=,tag:26uMp2JmVAckySIaL2BLCg==,type:float]
booleans:
- ENC[AES256_GCM,data:bCdz2A==,iv:8kD+h1jClyVHBj9o2WZuAkjk+uD6A2lgNpcGljpQEhk=,tag:u3/fktl5HfFrVLERVvLRGw==,type:bool]
- ENC[AES256_GCM,data:SgBh7wY=,iv:0s9Q9pQWbsZm2yHsmFalCzX0IqNb6ZqeY6QQYCWc+qU=,tag:OZb76BWCKbDLbcil4c8fYA==,type:bool]"
}
.to_string()
}
}

impl<S: RopsFileState> MockTestUtil for RopsFileMap<S, YamlFileFormat>
where
Self: MockFileFormatUtil<YamlFileFormat>,
{
fn mock() -> Self {
serde_yaml::from_str(&Self::mock_format_display()).expect("mock yaml string not serializable")
}
}
}

mod metadata {
mod core {
use crate::*;

impl MockFileFormatUtil<YamlFileFormat> for RopsFileMetadata {
fn mock_format_display() -> String {
#[cfg(feature = "age")]
{
let age_metadata_yaml_string = RopsFileAgeMetadata::mock_format_display();
let (first_line, remaining_lines) = age_metadata_yaml_string
.split_once('\n')
.expect("no newline delimeter in yaml age metadata");
indoc::formatdoc! {"
age:
- {}
{}",
first_line,
textwrap::indent(remaining_lines, " ")
}
}
}
}
}

#[cfg(feature = "age")]
mod age {
use crate::*;

impl MockFileFormatUtil<YamlFileFormat> for RopsFileAgeMetadata {
fn mock_format_display() -> String {
indoc::formatdoc! {"
recipient: {}
enc: |
{}",
AgeIntegration::mock_public_key_str(),
textwrap::indent(AgeIntegration::mock_encrypted_data_key_str()," ")
}
}
}
}
}
28 changes: 28 additions & 0 deletions crates/lib/src/rops_file/format/yaml/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::*;

#[derive(Debug, PartialEq)]
pub struct YamlFileFormat;

impl FileFormat for YamlFileFormat {
type Map = serde_yaml::Mapping;
type SerializeError = serde_yaml::Error;
type DeserializeError = serde_yaml::Error;

fn serialize_to_string<T: serde::Serialize>(t: &T) -> Result<String, Self::SerializeError> {
serde_yaml::to_string(t)
}

fn deserialize_from_str<T: serde::de::DeserializeOwned>(str: &str) -> Result<T, Self::DeserializeError> {
serde_yaml::from_str(str)
}
}

mod encrypted_map_to_tree {}

mod decrypted_map_to_tree;

#[cfg(feature = "test-utils")]
mod mock;

#[cfg(test)]
mod tests;
Loading

0 comments on commit e998d39

Please sign in to comment.