@atomist/sdm
Version:
Atomist Software Delivery Machine SDK
117 lines (105 loc) • 3.72 kB
text/typescript
/*
* Copyright © 2019 Atomist, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { PushMapping } from "../mapping/PushMapping";
import { PushTest } from "../mapping/PushTest";
import { PushRules } from "../mapping/support/PushRules";
import { allSatisfied } from "../mapping/support/pushTestUtils";
/**
* Simple DSL to create a decision tree.
* Trees and subtrees can compute variables as interim values for future use.
* Example usage, showing computed state:
*
* ```
* let count = 0; // Initialize a counter we'll use later
* const pm: PushMapping<Goals> = given<Goals>(TruePushTest, ...) // Use any push tests
* .init(() => count = 0) // Init to set state
* .itMeans("no frogs coming")
* .then(
* given<Goals>(TruePushTest, ...).itMeans("case1")
* .compute(() => count++) // Increment the counter for this branch
* .then(
* // Compute terminal rules
* whenPushSatisfies(count > 0, FalsePushTest).itMeans("nope").setGoals(NoGoals),
* whenPushSatisfies(TruePushTest).itMeans("yes").setGoals(HttpServiceGoals),
* ),
* );
* ```
* @param givenPushTests PushTests
* @return interim DSL structure
*/
export function given<V>(...givenPushTests: PushTest[]): TreeContext<V> {
return new TreeContext<V>(givenPushTests);
}
export class TreeContext<V> {
constructor(private readonly givenPushTests: PushTest[]) {}
/**
* Perform any computation necessary to initialize this branch:
* for example, setting variables in scope
* @param {(t: this) => any} f
* @return {any}
*/
public init(f: (t: this) => any): any {
f(this);
return this;
}
public itMeans(name: string): GivenTree<V> {
const givenPushTest = allSatisfied(...this.givenPushTests);
return new GivenTree<V>(givenPushTest, name);
}
}
/**
* Tree. Can compute variables
*/
export class GivenTree<V> {
constructor(private readonly givenPushTest: PushTest, private readonly name: string) {}
/**
* Perform computation before continuing.
* Typically used to set values that will be used in predicate expressions.
* @param {(t: this) => any} f
* @return {any}
*/
public compute(f: (t: this) => any): any {
f(this);
return this;
}
/**
* Set the resolution value of this tree
* @param {V} value
* @return {PushMapping<V>}
*/
public set(value: V): PushMapping<V> {
return {
name: this.name,
mapping: async () => value,
};
}
/**
* Enter a subtree of a number of mappings. Can be use
* to nest trees to arbitrary depth.
* @param {PushMapping<V>} pushMappings
* @return {PushMapping<V>}
*/
public then(...pushMappings: Array<PushMapping<V>>): PushMapping<V> {
const rules = new PushRules<V>(this.name, pushMappings);
return {
name: this.name,
mapping: async pli => {
const eligible = await this.givenPushTest.mapping(pli);
return eligible ? rules.mapping(pli) : undefined;
},
};
}
}