tf_provider/
attribute_path.rs

1// This file is part of the tf-provider project
2//
3// Copyright (C) ANEO, 2024-2024. All rights reserved.
4//
5// Licensed under the Apache License, Version 2.0 (the "License")
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9//     http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17//! [`AttributePath`] module
18
19use std::{borrow::Cow, fmt::Display};
20
21use crate::tfplugin6;
22
23/// Represent the path to an attribute
24///
25/// # Example
26///
27/// ```
28/// # use tf_provider::AttributePath;
29/// let path = AttributePath::new("foo").key("bar").attribute("array").index(1);
30/// // foo["bar"].array[1]
31/// ```
32#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
33pub struct AttributePath {
34    pub steps: Vec<AttributePathStep>,
35}
36
37impl AttributePath {
38    /// Create a new attribute path with the `root` attribute
39    ///
40    /// # Arguments
41    ///
42    /// * `name` - Name of the root attribute
43    pub fn new<T: Into<Cow<'static, str>>>(name: T) -> Self {
44        Self {
45            steps: vec![AttributePathStep::Attribute(name.into())],
46        }
47    }
48
49    /// Create a new attribute path without any path component
50    pub fn root() -> Self {
51        Self::default()
52    }
53
54    /// Create a new attribute path for a function argument
55    ///
56    /// # Arguments
57    ///
58    /// * `index` - index of the function argument
59    pub fn function_argument(index: i64) -> Self {
60        Self {
61            steps: vec![AttributePathStep::Index(index)],
62        }
63    }
64
65    /// Create a new attribute path where the attribute `.name` has been appended
66    ///
67    /// # Arguments
68    ///
69    /// * `name` - name of the attribute
70    pub fn attribute<T: Into<Cow<'static, str>>>(mut self, name: T) -> Self {
71        self.add_attribute(name);
72        self
73    }
74
75    /// Create a new attribute path where the access `["key"]` has been appended
76    ///
77    /// # Arguments
78    ///
79    /// * `key` - string subscript
80    pub fn key<T: Into<Cow<'static, str>>>(mut self, key: T) -> Self {
81        self.add_key(key);
82        self
83    }
84
85    /// Create a new attribute path where the access `[idx]` has been appended
86    ///
87    /// # Arguments
88    ///
89    /// * `idx` - integer subscript
90    pub fn index<T: Into<i64>>(mut self, idx: T) -> Self {
91        self.add_index(idx);
92        self
93    }
94
95    /// Add name access to the path (ie: `.name`)
96    ///
97    /// # Arguments
98    ///
99    /// * `name` - name of the attribute
100    pub fn add_attribute<T: Into<Cow<'static, str>>>(&mut self, name: T) -> &mut Self {
101        self.steps.push(AttributePathStep::Attribute(name.into()));
102        self
103    }
104
105    /// add key access to the path (ie: `["key"]`)
106    ///
107    /// # Arguments
108    ///
109    /// * `key` - string subscript
110    pub fn add_key<T: Into<Cow<'static, str>>>(&mut self, key: T) -> &mut Self {
111        self.steps.push(AttributePathStep::Key(key.into()));
112        self
113    }
114
115    /// add index access to the path (ie: `[idx]`)
116    ///
117    /// # Arguments
118    ///
119    /// * `idx` - integer subscript
120    pub fn add_index<T: Into<i64>>(&mut self, idx: T) -> &mut Self {
121        self.steps.push(AttributePathStep::Index(idx.into()));
122        self
123    }
124
125    /// Add step to the path
126    ///
127    /// # Arguments
128    ///
129    /// * `step` - step to add
130    pub fn add_step(&mut self, step: AttributePathStep) -> &mut Self {
131        self.steps.push(step);
132        self
133    }
134
135    /// Add multiple steps into the path
136    ///
137    /// # Arguments
138    ///
139    /// * `steps` - steps to add
140    pub fn add_steps(&mut self, mut steps: AttributePath) -> &mut Self {
141        self.steps.append(&mut steps.steps);
142        self
143    }
144}
145
146impl Display for AttributePath {
147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148        let mut sep = "";
149        for step in &self.steps {
150            match step {
151                AttributePathStep::Attribute(name) => {
152                    f.write_fmt(format_args!("{}{}", sep, name))?
153                }
154                AttributePathStep::Key(key) => f.write_fmt(format_args!("[{:?}]", key))?,
155                AttributePathStep::Index(idx) => f.write_fmt(format_args!("[{}]", idx))?,
156            }
157            sep = ".";
158        }
159        Ok(())
160    }
161}
162
163impl std::ops::AddAssign<AttributePathStep> for AttributePath {
164    fn add_assign(&mut self, rhs: AttributePathStep) {
165        self.steps.push(rhs);
166    }
167}
168
169impl std::ops::Add<AttributePathStep> for AttributePath {
170    type Output = Self;
171    fn add(mut self, rhs: AttributePathStep) -> Self::Output {
172        self += rhs;
173        self
174    }
175}
176
177impl From<AttributePathStep> for AttributePath {
178    fn from(value: AttributePathStep) -> Self {
179        Self { steps: vec![value] }
180    }
181}
182
183impl From<AttributePath> for tfplugin6::AttributePath {
184    fn from(value: AttributePath) -> Self {
185        Self {
186            steps: value.steps.into_iter().map(|step| step.into()).collect(),
187        }
188    }
189}
190
191/// Single step of an [`AttributePath`]
192#[derive(Clone, PartialEq, Eq, Hash, Debug)]
193pub enum AttributePathStep {
194    /// Attribute access: `.foo`
195    Attribute(Cow<'static, str>),
196    /// String subscript: `["foo"]`
197    Key(Cow<'static, str>),
198    /// Integer subscript: `[1]`
199    Index(i64),
200}
201
202impl Display for AttributePathStep {
203    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204        match self {
205            AttributePathStep::Attribute(name) => f.write_str(name.as_ref()),
206            AttributePathStep::Key(key) => f.write_fmt(format_args!("[{:?}]", key)),
207            AttributePathStep::Index(idx) => f.write_fmt(format_args!("[{}]", idx)),
208        }
209    }
210}
211
212impl std::ops::Add<AttributePathStep> for AttributePathStep {
213    type Output = AttributePath;
214    fn add(self, rhs: AttributePathStep) -> Self::Output {
215        AttributePath {
216            steps: vec![self, rhs],
217        }
218    }
219}
220
221impl From<AttributePathStep> for tfplugin6::attribute_path::Step {
222    fn from(value: AttributePathStep) -> Self {
223        use tfplugin6::attribute_path::step::Selector;
224        Self {
225            selector: Some(match value {
226                AttributePathStep::Attribute(name) => Selector::AttributeName(name.into_owned()),
227                AttributePathStep::Key(key) => Selector::ElementKeyString(key.into_owned()),
228                AttributePathStep::Index(idx) => Selector::ElementKeyInt(idx),
229            }),
230        }
231    }
232}