Skip to content

Commit

Permalink
Wrap FileFormat::Map in type state.
Browse files Browse the repository at this point in the history
  • Loading branch information
gibbz00 committed Dec 16, 2023
1 parent cfbe87d commit 658665b
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 49 deletions.
16 changes: 10 additions & 6 deletions crates/lib/src/rops_file/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ use serde::{Deserialize, Serialize};
use crate::*;

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(bound = "F: FileFormat")]
pub struct RopsFile<S: RopsFileState, F: FileFormat> {
#[serde(flatten)]
pub map: F::Map,
pub map: RopsFileMap<S, F>,
#[serde(rename = "sops")]
pub metadata: RopsFileAgeMetadata,
#[serde(skip)]
Expand All @@ -18,13 +19,13 @@ pub struct RopsFile<S: RopsFileState, F: FileFormat> {
mod mock {
use super::*;

impl<F: FileFormat> MockTestUtil for RopsFile<Decrypted, F>
impl<S: RopsFileState, F: FileFormat> MockTestUtil for RopsFile<S, F>
where
F::Map: MockTestUtil,
RopsFileMap<S, F>: MockTestUtil,
{
fn mock() -> Self {
Self {
map: F::Map::mock(),
map: MockTestUtil::mock(),
metadata: MockTestUtil::mock(),
state_marker: PhantomData,
}
Expand All @@ -35,13 +36,16 @@ mod mock {
mod yaml {
use super::*;

impl MockFileFormatUtil<YamlFileFormat> for RopsFile<Decrypted, YamlFileFormat> {
impl<S: RopsFileState> MockFileFormatUtil<YamlFileFormat> for RopsFile<S, YamlFileFormat>
where
RopsFileMap<S, YamlFileFormat>: MockFileFormatUtil<YamlFileFormat>,
{
fn mock_format_display() -> String {
indoc::formatdoc! {"
{}
sops:
{}",
<YamlFileFormat as FileFormat>::Map::mock_format_display(),
RopsFileMap::mock_format_display(),
textwrap::indent(&RopsFileAgeMetadata::mock_format_display()," ")
}
}
Expand Down
4 changes: 3 additions & 1 deletion crates/lib/src/rops_file/format/core.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::fmt::Debug;

use serde::{de::DeserializeOwned, Serialize};

pub trait FileFormat {
type Map;
type Map: Serialize + DeserializeOwned + PartialEq + Debug;
type SerializeError: std::error::Error + Send + Sync + 'static;
type DeserializeError: std::error::Error + Send + Sync + 'static;

Expand Down
61 changes: 61 additions & 0 deletions crates/lib/src/rops_file/map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use std::marker::PhantomData;

use serde::{Deserialize, Serialize};

use crate::*;

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct RopsFileMap<S: RopsFileState, F: FileFormat> {
#[serde(flatten)]
inner: F::Map,
#[serde(skip)]
state_marker: PhantomData<S>,
}

impl<S: RopsFileState, F: FileFormat> RopsFileMap<S, F> {
pub fn into_inner_map(self) -> F::Map {
self.inner
}

#[cfg(feature = "test-utils")]
pub fn from_inner_map(inner: F::Map) -> Self {
Self {
inner,
state_marker: PhantomData,
}
}
}

#[cfg(feature = "test-utils")]
mod mock {
#[cfg(feature = "yaml")]
mod yaml {
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()
}
}

impl MockTestUtil for RopsFileMap<Decrypted, YamlFileFormat> {
fn mock() -> Self {
serde_yaml::from_str(&Self::mock_format_display()).expect("mock yaml string not serializable")
}
}
}
}
9 changes: 6 additions & 3 deletions crates/lib/src/rops_file/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
mod core;
pub use core::RopsFile;

mod map;
pub use map::RopsFileMap;

mod key_path;
pub use key_path::KeyPath;

Expand All @@ -10,9 +16,6 @@ pub use tree::{RopsTree, RopsTreeBuildError};
mod metadata;
pub use metadata::*;

mod core;
pub use core::RopsFile;

mod state;
pub use state::{Decrypted, Encrypted, RopsFileState};

Expand Down
51 changes: 12 additions & 39 deletions crates/lib/src/rops_file/tree/yaml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ use serde_yaml::{Mapping as YamlMap, Value as YamlValue};

use crate::*;

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

fn try_from(yaml_map: YamlMap) -> Result<Self, Self::Error> {
return recursive_map_call(yaml_map);
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>, RopsTreeBuildError> {
let mut inner_map = IndexMap::default();
Expand Down Expand Up @@ -54,59 +54,32 @@ impl TryFrom<YamlMap> for RopsTree<Decrypted> {
}
}

#[cfg(feature = "test-utils")]
mod mock {
use super::*;

impl MockFileFormatUtil<YamlFileFormat> for YamlMap {
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()
}
}

impl MockTestUtil for YamlMap {
fn mock() -> Self {
serde_yaml::from_str(&YamlMap::mock_format_display()).expect("mock yaml string not serializable")
}
}
}

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

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

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

#[test]
fn dissallows_out_of_range_integers() {
let yaml_map = serde_yaml::from_str::<YamlMap>(&format!("invalid_integer: {}", u64::MAX)).unwrap();
let file_map = RopsFileMap::from_inner_map(serde_yaml::from_str::<YamlMap>(&format!("invalid_integer: {}", u64::MAX)).unwrap());
assert!(matches!(
RopsTree::try_from(yaml_map).unwrap_err(),
RopsTree::try_from(file_map).unwrap_err(),
RopsTreeBuildError::IntegerOutOfRange(_)
))
}
Expand Down

0 comments on commit 658665b

Please sign in to comment.