openjd_model/template/job_template.rs
1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// Copyright by contributors to this project.
3// SPDX-License-Identifier: (Apache-2.0 OR MIT)
4
5//! Job template per spec §1.1.
6
7use super::constrained_strings::{Description, ExtensionName};
8use super::environment::Environment;
9use super::parameters::JobParameterDefinition;
10use super::step::StepTemplate;
11use crate::format_string::FormatString;
12use serde::Deserialize;
13
14/// §1.1 JobTemplate
15#[derive(Debug, Clone, Deserialize)]
16#[serde(rename_all = "camelCase", deny_unknown_fields)]
17pub struct JobTemplate {
18 pub specification_version: String,
19 #[serde(rename = "$schema")]
20 pub schema: Option<String>,
21 pub extensions: Option<Vec<ExtensionName>>,
22 pub name: FormatString,
23 pub description: Option<Description>,
24 pub parameter_definitions: Option<Vec<JobParameterDefinition>>,
25 pub job_environments: Option<Vec<Environment>>,
26 pub steps: Vec<StepTemplate>,
27}
28
29impl JobTemplate {
30 pub fn name(&self) -> &FormatString {
31 &self.name
32 }
33
34 pub fn description(&self) -> Option<&str> {
35 self.description.as_ref().map(|d| d.0.as_str())
36 }
37
38 pub fn parameter_definitions_list(&self) -> &[JobParameterDefinition] {
39 match &self.parameter_definitions {
40 Some(defs) => defs,
41 None => &[],
42 }
43 }
44
45 /// Derive the [`ModelProfile`](crate::ModelProfile) described by
46 /// this template: the revision from `specificationVersion` and the
47 /// extensions set declared on the template.
48 ///
49 /// Entries in the `extensions` list that don't parse as a known
50 /// [`ModelExtension`](crate::types::ModelExtension) are silently
51 /// skipped.
52 ///
53 /// This is the "what the template says it needs" profile — the one
54 /// to pass to sessions, to
55 /// [`ModelProfile::to_expr_profile`](crate::ModelProfile::to_expr_profile),
56 /// or to wrap in a
57 /// [`ValidationContext`](crate::types::ValidationContext) when
58 /// calling `create_job`.
59 pub fn profile(&self) -> crate::ModelProfile {
60 use std::str::FromStr;
61 let revision =
62 crate::types::TemplateSpecificationVersion::from_str(&self.specification_version)
63 .map(|v| v.revision())
64 // Unknown spec versions shouldn't reach this point (the template
65 // was validated). Fall back to the first revision.
66 .unwrap_or(crate::types::SpecificationRevision::V2023_09);
67 let mut exts = crate::types::Extensions::new();
68 if let Some(list) = &self.extensions {
69 for e in list {
70 if let Ok(known) = crate::types::ModelExtension::from_str(e.as_str()) {
71 exts.insert(known);
72 }
73 }
74 }
75 crate::ModelProfile::new(revision).with_extensions(exts)
76 }
77
78 /// Convenience: wrap [`profile`](Self::profile) in a
79 /// [`ValidationContext`](crate::types::ValidationContext) with
80 /// default caller limits. Equivalent to
81 /// `ValidationContext::from_profile(self.profile())`.
82 ///
83 /// This is the convenient "do what the template says" context for
84 /// callers that do not want to override revision/extension policy.
85 /// Callers that *do* want to override (e.g. a service stripping EXPR
86 /// regardless of template intent) should build a
87 /// `ValidationContext` explicitly and use
88 /// [`with_caller_limits`](crate::types::ValidationContext::with_caller_limits)
89 /// as needed.
90 pub fn default_validation_context(&self) -> crate::types::ValidationContext {
91 crate::types::ValidationContext::from_profile(self.profile())
92 }
93}