Skip to content

Commit

Permalink
Merge pull request #1 from mrijken/feature/refactor
Browse files Browse the repository at this point in the history
Feature/refactor
  • Loading branch information
mrijken authored Sep 11, 2023
2 parents 2a16ae3 + e3d5976 commit bab32a3
Show file tree
Hide file tree
Showing 94 changed files with 1,569 additions and 1,658 deletions.
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# CHANGES

## 0.4.0

- Refactor checkers
- Add file regex

## 0.3.3

- Add documentation
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "check-config"
version = "0.3.5"
version = "0.4.0"
edition = "2021"
license = "MIT"
homepage = "https://pypi.org/project/check-config/"
Expand Down
22 changes: 22 additions & 0 deletions docs/checkers.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ checker types (and more to come):
|------|-------------|---------|
| [file_absent](#file-absent) | the file must be absent | yes |
| [file_present](#file-present) | the file must be present, indifferent the content | yes |
| [file_regex_match](#file-regex-match) | the content of the file must match the regex expression | no |
| [key_absent](#key-absent) | a specified key must be absent in a toml / yaml / json file | yes |
| [key_value_present](#key-value-present) | a specified key with a specified value must be present in a toml / yaml / json file | yes |
| [key_value_regex_match](#key-value-regex-match) | the value of a specified key must be match the specified regex in a toml / yaml / json file | no |
Expand Down Expand Up @@ -90,6 +91,27 @@ The key can be nested. In the next case it is sufficient that `key` is not prese

This checker type can handle different kind of [mapping file types](#mapping-file-types)

## File Regex Match

Checks whether the contents of the file matches the regex expression.

```toml
[[".bashrc".file_regex_match]]
__regex__ = "export KEY=.*"

[[".bashrc".file_regex_match]]
__regex__ = "export ANOTHER_KEY=.*"
```

Multiple regex can be given when the file.checker_type pair is an array, ie:

```toml
[".bashrc".file_regex_match]
__regex__ = "export KEY=.*"
```

This checker type can handle any text file.

## Key Value Present

`key_value_present` will check that the keys specified are present with the specified values.
Expand Down
7 changes: 7 additions & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,10 @@ __items__ = [
[[".gitignore".lines_present]]
__lines__ = ".mypy_cache"
```

## Bashrc

```toml
[".bashrc".file_regex_match]
__regex__ = "export KEY=.*"
```
2 changes: 2 additions & 0 deletions example_checkers/bash.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[".bashrc".file_regex_match]
__regex__ = "export KEY=.*"
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "maturin"

[project]
name = "check_config"
version = "0.3.5"
version = "0.4.0"
description = "Check configuration files."
authors = [{ name = "Marc Rijken", email = "[email protected]" }]
maintainers = [{ name = "Marc Rijken", email = "[email protected]" }]
Expand Down
16 changes: 12 additions & 4 deletions src/checkers/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use super::GenericCheck;
pub(crate) enum Action {
RemoveFile,
SetContents(String),
MatchRegex { key: String, regex: String },
MatchKeyRegex { key: String, regex: String },
MatchFileRegex { regex: String },
None,
}

Expand Down Expand Up @@ -84,10 +85,10 @@ pub(crate) trait Check: DebugTrait {
false,
None,
Some(&format!(
"Set file contents to: {}",
"Set file contents to: \n{}",
TextDiff::from_lines(
self.generic_check()
.get_file_contents(Some("".to_string()))
.get_file_contents(super::DefaultContent::EmptyString)
.unwrap_or("".to_string())
.as_str(),
new_contents.as_str()
Expand All @@ -96,13 +97,20 @@ pub(crate) trait Check: DebugTrait {
)),
);
}
Action::MatchRegex { key, regex } => {
Action::MatchKeyRegex { key, regex } => {
self.print(
false,
Some(&key),
Some(&format!("Make sure value matches regex {}", regex)),
);
}
Action::MatchFileRegex { regex } => {
self.print(
false,
None,
Some(&format!("Make sure value matches regex {}", regex)),
);
}
}

Ok(action)
Expand Down
89 changes: 82 additions & 7 deletions src/checkers/entry_absent.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::mapping::generic::Mapping;

use super::{
base::{Action, Check, CheckError},
GenericCheck,
DefaultContent, GenericCheck,
};

#[derive(Debug)]
Expand Down Expand Up @@ -30,13 +32,12 @@ impl Check for EntryAbsent {
fn get_action(&self) -> Result<Action, CheckError> {
let contents = self
.generic_check()
.get_file_contents(Some("".to_string()))?;
.get_file_contents(DefaultContent::EmptyString)?;
let mut doc = self.generic_check().get_mapping()?;

let new_contents = self
.generic_check()
.file_type()?
.remove_entries(&contents, &self.value)
.unwrap();
remove_entries(doc.as_mut(), &self.value);

let new_contents = doc.to_string()?;

if contents == new_contents {
Ok(Action::None)
Expand All @@ -45,3 +46,77 @@ impl Check for EntryAbsent {
}
}
}

fn remove_entries(doc: &mut dyn Mapping, entries_to_remove: &toml::Table) {
for (key_to_remove, value_to_remove) in entries_to_remove {
if !value_to_remove.is_table() {
panic!("Unexpected value type");
}
let value_to_remove = value_to_remove.as_table().unwrap();
if value_to_remove.contains_key("__items__") {
let doc_array = match doc.get_array(key_to_remove, false) {
Ok(a) => a,
Err(_) => panic!("expecting key to exist"),
};

for item in value_to_remove
.get("__items__")
.unwrap()
.as_array()
.unwrap()
{
doc_array.remove(item)
}
continue;
}
let child_doc = doc.get_mapping(key_to_remove, false).unwrap();
remove_entries(child_doc, value_to_remove);
}
}

#[cfg(test)]
mod tests {
use crate::checkers::test_helpers::read_test_files;
use crate::file_types::{self, FileType};

use super::*;

#[test]
fn test_test_files() {
for (test_path, test_input, test_expected_output, checker) in
read_test_files("entry_absent")
{
let mut test_input = test_input;
remove_entries(test_input.as_mut(), checker.as_table().unwrap());

assert_eq!(
*test_expected_output,
test_input.to_string().unwrap(),
"test_path {} failed",
test_path
);
}
}

#[test]
fn test_remove_entries_with_tables() {
let entries_to_remove = r#"
[key.list]
__items__ = [{key = "3"}, {key = "2"}, {key = "4"}]
"#;
let entries_to_remove = toml::from_str::<toml::Value>(entries_to_remove).unwrap();
let entries_to_remove = entries_to_remove.as_table().unwrap();

let toml_contents = r#"[key]
list = [{key = "1"}, {key = "2"}]
"#;
let toml_new_contents = "[key]\nlist = [{key = \"1\"}]\n";

let mut toml_doc = file_types::toml::Toml::new()
.to_mapping(toml_contents)
.unwrap();
remove_entries(toml_doc.as_mut(), entries_to_remove);

assert_eq!(toml_new_contents, toml_doc.to_string().unwrap());
}
}
61 changes: 54 additions & 7 deletions src/checkers/entry_present.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::mapping::generic::Mapping;

use super::{
base::{Action, Check, CheckError},
GenericCheck,
DefaultContent, GenericCheck,
};

#[derive(Debug)]
Expand Down Expand Up @@ -30,13 +32,12 @@ impl Check for EntryPresent {
fn get_action(&self) -> Result<Action, CheckError> {
let contents = self
.generic_check()
.get_file_contents(Some("".to_string()))?;
.get_file_contents(DefaultContent::EmptyString)?;
let mut doc = self.generic_check().get_mapping()?;

let new_contents = self
.generic_check()
.file_type()?
.add_entries(&contents, &self.value)
.unwrap();
add_entries(doc.as_mut(), &self.value);

let new_contents = doc.to_string()?;

if contents == new_contents {
Ok(Action::None)
Expand All @@ -45,3 +46,49 @@ impl Check for EntryPresent {
}
}
}

fn add_entries(doc: &mut dyn Mapping, entries_to_add: &toml::map::Map<String, toml::Value>) {
for (key_to_add, value_to_add) in entries_to_add {
if !value_to_add.is_table() {
panic!("Unexpected value type");
}
let table_to_add = value_to_add.as_table().unwrap();
if table_to_add.contains_key("__items__") {
let doc_array = doc
.get_array(key_to_add, true)
.expect("expecting key to exist");

for item in table_to_add.get("__items__").unwrap().as_array().unwrap() {
doc_array.insert_when_not_present(item)
}
continue;
}

let child_doc = doc.get_mapping(key_to_add, true).unwrap();
add_entries(child_doc, table_to_add);
}
}

#[cfg(test)]
mod tests {
use crate::checkers::test_helpers::read_test_files;

use super::*;

#[test]
fn test_test_files() {
for (test_path, test_input, test_expected_output, checker) in
read_test_files("entry_present")
{
let mut test_input = test_input;
add_entries(test_input.as_mut(), checker.as_table().unwrap());

assert_eq!(
*test_expected_output,
test_input.to_string().unwrap(),
"test_path {} failed",
test_path
);
}
}
}
Loading

0 comments on commit bab32a3

Please sign in to comment.