Skip to content

Commit

Permalink
Work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
gpoblon committed Oct 13, 2020
1 parent eac52f9 commit f387f83
Show file tree
Hide file tree
Showing 13 changed files with 314 additions and 153 deletions.
13 changes: 8 additions & 5 deletions rudder-lang/src/generator/cfengine.rs
Expand Up @@ -94,8 +94,7 @@ impl CFEngine {
}
EnumExpressionPart::Compare(var, e, item) => {
if let Some(true) = gc.enum_list.enum_is_global(*e) {
// FIXME: We need some translation here since not all enums are available in cfengine (ex debian_only)
item.fragment().to_string() // here
gc.enum_list.get_item_cfengine_name(*var, *item)
} else {
// concat var name + item
let prefix = &self.var_prefixes[var.fragment()];
Expand Down Expand Up @@ -178,10 +177,12 @@ impl CFEngine {
}
Statement::Fail(msg) => Ok(vec![Promise::usebundle(
"_abort",
None,
vec![quoted("policy_fail"), self.value_to_string(msg, true)?],
)]),
Statement::LogDebug(msg) => Ok(vec![Promise::usebundle(
"log_rudder_mode",
None,
vec![
quoted("log_debug"),
self.value_to_string(msg, true)?,
Expand All @@ -192,6 +193,7 @@ impl CFEngine {
)]),
Statement::LogInfo(msg) => Ok(vec![Promise::usebundle(
"log_rudder_mode",
None,
vec![
quoted("log_info"),
self.value_to_string(msg, true)?,
Expand All @@ -202,6 +204,7 @@ impl CFEngine {
)]),
Statement::LogWarn(msg) => Ok(vec![Promise::usebundle(
"log_rudder_mode",
None,
vec![
quoted("log_warn"),
self.value_to_string(msg, true)?,
Expand All @@ -217,11 +220,11 @@ impl CFEngine {
Some(c) => format!("!({})", c),
});
Ok(vec![if *outcome == Token::new("", "kept") {
Promise::usebundle("success", vec![])
Promise::usebundle("success", None, vec![])
} else if *outcome == Token::new("", "repaired") {
Promise::usebundle("repaired", vec![])
Promise::usebundle("repaired", None, vec![])
} else {
Promise::usebundle("error", vec![])
Promise::usebundle("error", None, vec![])
}])
}
Statement::Noop => Ok(vec![]),
Expand Down
59 changes: 41 additions & 18 deletions rudder-lang/src/generator/cfengine/syntax.rs
Expand Up @@ -22,7 +22,7 @@ pub fn quoted(s: &str) -> String {
format!("\"{}\"", s)
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum PromiseType {
Vars,
Methods,
Expand All @@ -43,7 +43,7 @@ impl fmt::Display for PromiseType {
}
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum AttributeType {
UseBundle,
Unless,
Expand Down Expand Up @@ -100,10 +100,12 @@ impl fmt::Display for AttributeType {
}
}

#[derive(Clone, PartialEq, Eq)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Promise {
/// Comments in output file
comments: Vec<String>,
/// Resource/state the promise calls
component: Option<String>,
/// Type of the promise
promise_type: PromiseType,
/// Target of the promise
Expand All @@ -120,9 +122,14 @@ const FALSE_CLASSES: [&str; 4] = ["false", "!any", "concat(\"!any\")", "concat(\

/// Promise constructors should allow avoiding common mistakes
impl Promise {
pub fn new<T: Into<String>>(promise_type: PromiseType, promiser: T) -> Self {
pub fn new<T: Into<String>>(
promise_type: PromiseType,
component: Option<String>,
promiser: T,
) -> Self {
Self {
promise_type,
component,
promiser: promiser.into(),
attributes: HashMap::new(),
comments: vec![],
Expand All @@ -145,12 +152,12 @@ impl Promise {

/// Shortcut for building a string variable with a raw value
pub fn string_raw<T: Into<String>, S: AsRef<str>>(name: T, value: S) -> Self {
Promise::new(PromiseType::Vars, name).attribute(AttributeType::String, value.as_ref())
Promise::new(PromiseType::Vars, None, name).attribute(AttributeType::String, value.as_ref())
}

/// Shortcut for building an slist variable with a list of values to be quoted
pub fn slist<T: Into<String>, S: AsRef<str>>(name: T, values: Vec<S>) -> Self {
Promise::new(PromiseType::Vars, name).attribute(
Promise::new(PromiseType::Vars, None, name).attribute(
AttributeType::Slist,
format!(
"{{{}}}",
Expand All @@ -164,8 +171,17 @@ impl Promise {
}

/// Shortcut for calling a bundle with parameters
pub fn usebundle<T: AsRef<str>>(bundle: T, parameters: Vec<String>) -> Self {
Promise::new(PromiseType::Methods, "method_call").attribute(
pub fn usebundle<T: AsRef<str>>(
bundle: T,
component: Option<&str>,
parameters: Vec<String>,
) -> Self {
Promise::new(
PromiseType::Methods,
component.map(|s| s.to_owned()),
"method_call",
)
.attribute(
AttributeType::UseBundle,
format!("{}({})", bundle.as_ref(), parameters.join(", ")),
)
Expand Down Expand Up @@ -218,7 +234,10 @@ impl Promise {
fn format(&self, index: usize, padding: usize) -> String {
let promiser = if self.promise_type == PromiseType::Methods {
// Methods need to be unique
format!("{}_{}", UNIQUE_ID, index)
match self.component.clone() {
Some(method) => format!("{}_{}_{}", method, UNIQUE_ID, index),
None => format!("{}_{}", UNIQUE_ID, index),
}
} else {
self.promiser.clone()
};
Expand Down Expand Up @@ -342,19 +361,23 @@ impl Method {
// Reporting context
let reporting_context = Promise::usebundle(
"_method_reporting_context",
Some(&self.report_component),
vec![
quoted(&self.report_component),
quoted(&self.report_parameter),
],
)
.comment(format!("{}:", self.report_component,))
.comment(format!("{}:", self.report_component))
.comment("")
.comment(format!(" {}", self.source,))
.comment(format!(" {}", self.source))
.comment("");

// Actual method call
let method =
Promise::usebundle(format!("{}_{}", self.resource, self.state), self.parameters);
let method = Promise::usebundle(
format!("{}_{}", self.resource, self.state),
Some(&self.report_component),
self.parameters,
);

if has_condition {
let na_condition = format!(
Expand All @@ -367,11 +390,11 @@ impl Method {
method.if_condition(self.condition.clone()),
// NA report
Promise::usebundle(
"_classes_noop",
"_classes_noop", Some(&self.report_component),
vec![na_condition.clone()],
)
.unless_condition(&self.condition),
Promise::usebundle("log_rudder", vec![
Promise::usebundle("log_rudder", Some(&self.report_component), vec![
quoted(&format!("Skipping method '{}' with key parameter '{}' since condition '{}' is not reached", &self.report_component, &self.report_parameter, self.condition)),
quoted(&self.report_parameter),
na_condition.clone(),
Expand Down Expand Up @@ -556,12 +579,12 @@ mod tests {
#[test]
fn format_promise() {
assert_eq!(
Promise::new(PromiseType::Vars, "test")
Promise::new(PromiseType::Vars, None, "test")
.format(0, LONGUEST_ATTRIBUTE_LEN + 3 + UNIQUE_ID_LEN),
"\"test\";"
);
assert_eq!(
Promise::new(PromiseType::Vars, "test")
Promise::new(PromiseType::Vars, None, "test")
.comment("test".to_string())
.format(0, LONGUEST_ATTRIBUTE_LEN + 3 + UNIQUE_ID_LEN),
" # test\n\"test\";"
Expand Down Expand Up @@ -600,7 +623,7 @@ mod tests {
assert_eq!(
Bundle::agent("test")
.parameters(vec!["file".to_string(), "lines".to_string()])
.promise_group(vec![Promise::usebundle("test", vec![])])
.promise_group(vec![Promise::usebundle("test", None, vec![])])
.to_string(),
"bundle agent test(file, lines) {\n\n methods:\n \"${report_data.directive_id}_0\" usebundle => test();\n\n}"
);
Expand Down
4 changes: 1 addition & 3 deletions rudder-lang/src/generator/dsc.rs
Expand Up @@ -122,9 +122,7 @@ impl DSC {
}
EnumExpressionPart::Compare(var, e, item) => {
if let Some(true) = gc.enum_list.enum_is_global(*e) {
println!("some: {}", item.fragment());
// We probably need some translation here since not all enums are available in cfengine (ex debian_only)
item.fragment().to_string() // here
gc.enum_list.get_item_cfengine_name(*var, *item)
} else {
// Temporary keep this piece of code in case it has a purpose
// // concat var name + item
Expand Down
63 changes: 43 additions & 20 deletions rudder-lang/src/ir/enums.rs
Expand Up @@ -2,7 +2,8 @@
// SPDX-FileCopyrightText: 2019-2020 Normation SAS

use super::{
context::Type, context::VarContext,
context::Type,
context::VarContext,
enum_tree::{EnumItem, EnumTree},
};
use crate::{error::*, parser::*};
Expand Down Expand Up @@ -52,6 +53,15 @@ impl<'src> EnumList<'src> {
self.enums.get(&e).and_then(|e| e.item_metadata.get(&i))
}

/// Returns the item cfengine name (unchanged if no cfengine_name metadata)
pub fn get_item_cfengine_name(&self, var: Token<'src>, item: Token<'src>) -> String {
self.enum_item_metadata(var, item)
.and_then(|metadatas| metadatas.get("cfengine_name"))
.and_then(|cf_value| cf_value.as_str())
.unwrap_or(item.fragment())
.to_string()
}

/// Returns the item if it is global and None otherwise
pub fn global_enum(&self, e: Token<'src>) -> Option<&Token<'src>> {
self.global_items.get(&e)
Expand Down Expand Up @@ -176,7 +186,10 @@ impl<'src> EnumList<'src> {
// check for key name duplicate and insert reference
self.add_to_global(is_global, tree_name, &e.items)?;
// insert keys
self.enums.get_mut(&tree_name).expect("BUG: tree disapeared from 2 lines above").extend(e)?;
self.enums
.get_mut(&tree_name)
.expect("BUG: tree disapeared from 2 lines above")
.extend(e)?;
Ok(None)
}

Expand Down Expand Up @@ -240,8 +253,11 @@ impl<'src> EnumList<'src> {
}

/// Get variable enum type, if the variable doesn't exist or isn't an enum, return an error
fn get_var_enum(&self, context: &VarContext<'src>, variable: Token<'src>) -> Result<Token<'src>>
{
fn get_var_enum(
&self,
context: &VarContext<'src>,
variable: Token<'src>,
) -> Result<Token<'src>> {
match context.get_type(&variable) {
None => fail!(variable, "The variable {} doesn't exist", variable),
Some(Type::Enum(e)) => Ok(e),
Expand Down Expand Up @@ -273,8 +289,7 @@ impl<'src> EnumList<'src> {
tree_name: Option<Token<'src>>,
value: Token<'src>,
my_be_boolean: bool,
) -> Result<(Token<'src>, Token<'src>, Token<'src>)>
{
) -> Result<(Token<'src>, Token<'src>, Token<'src>)> {
match (variable, tree_name) {
(None, None) => {
if let Some(tree_name) = self.global_items.get(&value) {
Expand Down Expand Up @@ -339,8 +354,7 @@ impl<'src> EnumList<'src> {
&self,
context: &VarContext<'src>,
expr: PEnumExpression<'src>,
) -> Result<EnumExpression<'src>>
{
) -> Result<EnumExpression<'src>> {
let PEnumExpression { source, expression } = expr;
self.canonify_expression_part(context, expression)
.map(|x| EnumExpression {
Expand All @@ -353,8 +367,7 @@ impl<'src> EnumList<'src> {
&self,
context: &VarContext<'src>,
expr: PEnumExpressionPart<'src>,
) -> Result<EnumExpressionPart<'src>>
{
) -> Result<EnumExpressionPart<'src>> {
match expr {
PEnumExpressionPart::Default(t) => Ok(EnumExpressionPart::Default(t)),
PEnumExpressionPart::NoDefault(t) => Ok(EnumExpressionPart::NoDefault(t)),
Expand Down Expand Up @@ -525,10 +538,7 @@ struct VariableIterator<'it, 'src> {
}

impl<'it, 'src> VariableIterator<'it, 'src> {
fn new(
variable: Token<'src>,
items: &'it HashSet<EnumItem<'src>>,
) -> Self {
fn new(variable: Token<'src>, items: &'it HashSet<EnumItem<'src>>) -> Self {
let mut iterator = items.iter();
VariableIterator {
variable,
Expand Down Expand Up @@ -732,8 +742,12 @@ mod tests {
fn test_canonify() {
let mut elist = EnumList::new();
let mut context = VarContext::new(None);
context.add_variable_declaration("T".into(), Type::Enum("T".into())).expect("Test init");
context.add_variable_declaration("var".into(), Type::Enum("T".into())).expect("Test init");
context
.add_variable_declaration("T".into(), Type::Enum("T".into()))
.expect("Test init");
context
.add_variable_declaration("var".into(), Type::Enum("T".into()))
.expect("Test init");
elist
.add_enum(penum_t("global enum T { a, b, c }"))
.unwrap();
Expand Down Expand Up @@ -770,8 +784,12 @@ mod tests {
fn test_listvars() {
let mut elist = EnumList::new();
let mut context = VarContext::new(None);
context.add_variable_declaration("T".into(), Type::Enum("T".into())).expect("Test init");
context.add_variable_declaration("var".into(), Type::Enum("T".into())).expect("Test init");
context
.add_variable_declaration("T".into(), Type::Enum("T".into()))
.expect("Test init");
context
.add_variable_declaration("var".into(), Type::Enum("T".into()))
.expect("Test init");
elist
.add_enum(penum_t("global enum T { a, b, c }"))
.unwrap();
Expand Down Expand Up @@ -847,7 +865,9 @@ mod tests {
fn test_evaluation() {
let mut elist = EnumList::new();
let mut context = VarContext::new(None);
context.add_variable_declaration("T".into(), Type::Enum("T".into())).expect("Test init");
context
.add_variable_declaration("T".into(), Type::Enum("T".into()))
.expect("Test init");
elist
.add_enum(penum_t("global enum T { a, b, c }"))
.unwrap();
Expand Down Expand Up @@ -890,7 +910,10 @@ mod tests {
elist.evaluate(&cases1[..], Token::from("test1")),
Vec::new()
);
let cases2 = [(e2.clone(), Vec::new() as Vec<i32>), (e3.clone(), Vec::new())];
let cases2 = [
(e2.clone(), Vec::new() as Vec<i32>),
(e3.clone(), Vec::new()),
];
assert_eq!(elist.evaluate(&cases2[..], Token::from("test2")).len(), 1);
let cases3 = [
(e1.clone(), Vec::new() as Vec<i32>),
Expand Down
3 changes: 3 additions & 0 deletions rudder-lang/src/lib.rs
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2019-2020 Normation SAS

// TODO remove silence compilation warnings. Tmp, helps working on testing loop (cleaner)
#![allow(warnings, unused)]

// `macro_use` attributes make related macro available to the current scope...
// but only for modules bring into scope after the statement
// so it needs to be put first
Expand Down

0 comments on commit f387f83

Please sign in to comment.