Skip to main content

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}