From 5e03a9b75a8dac4f193bd14c6868c2ba077a0ad4 Mon Sep 17 00:00:00 2001 From: Oliver Wooding Date: Sat, 14 Mar 2026 16:07:15 +0100 Subject: [PATCH 1/3] intern FieldPath in salsa for O(1) clone and deduplication FieldPath is stored in every Blame value, making cheap clones important. Previously it was a plain Vec requiring O(n) clones. This change salsa-interns FieldPath (making it Copy) by erasing the FieldName lifetime through salsa::Id storage in PathSegment. Blame also becomes Copy as both its fields are now salsa-interned. Co-Authored-By: Claude Opus 4.6 --- crates/gnomon-db/src/eval.rs | 2 +- crates/gnomon-db/src/eval/desugar.rs | 2 +- crates/gnomon-db/src/eval/export.rs | 2 +- crates/gnomon-db/src/eval/interned.rs | 48 +++++++++++++++++---------- crates/gnomon-db/src/eval/lower.rs | 26 +++++++-------- crates/gnomon-db/src/eval/merge.rs | 2 +- crates/gnomon-db/src/eval/render.rs | 9 +++-- crates/gnomon-db/src/eval/rrule.rs | 2 +- crates/gnomon-db/src/eval/types.rs | 4 +-- 9 files changed, 57 insertions(+), 40 deletions(-) diff --git a/crates/gnomon-db/src/eval.rs b/crates/gnomon-db/src/eval.rs index 6901c8d..51027a9 100644 --- a/crates/gnomon-db/src/eval.rs +++ b/crates/gnomon-db/src/eval.rs @@ -454,7 +454,7 @@ mod tests { let r = expect_single_decl(&result); let uid_name = FieldName::new(&db, "uid".to_string()); let blamed_value = r.get(&db, &uid_name).unwrap(); - assert_eq!(blamed_value.blame.path.0.len(), 1); + assert_eq!(blamed_value.blame.path.segments(&db).len(), 1); } // ── Every expression lowering ──────────────────────────────── diff --git a/crates/gnomon-db/src/eval/desugar.rs b/crates/gnomon-db/src/eval/desugar.rs index 8be5c73..d98524e 100644 --- a/crates/gnomon-db/src/eval/desugar.rs +++ b/crates/gnomon-db/src/eval/desugar.rs @@ -241,7 +241,7 @@ mod tests { let decl_id = DeclId::new(db, source, 0, DeclKind::Event); Blame { decl: decl_id, - path: FieldPath::root(), + path: FieldPath::root(db), } } diff --git a/crates/gnomon-db/src/eval/export.rs b/crates/gnomon-db/src/eval/export.rs index 0ef8335..0af55f9 100644 --- a/crates/gnomon-db/src/eval/export.rs +++ b/crates/gnomon-db/src/eval/export.rs @@ -73,7 +73,7 @@ mod tests { let source = SourceFile::new(db, "/test".into(), String::new()); Blame { decl: DeclId::new(db, source, 0, DeclKind::Expr), - path: FieldPath::root(), + path: FieldPath::root(db), } } diff --git a/crates/gnomon-db/src/eval/interned.rs b/crates/gnomon-db/src/eval/interned.rs index 6183864..c67b20c 100644 --- a/crates/gnomon-db/src/eval/interned.rs +++ b/crates/gnomon-db/src/eval/interned.rs @@ -15,36 +15,50 @@ impl fmt::Debug for FieldName<'_> { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum PathSegment<'db> { - Field(FieldName<'db>), +/// A segment of a [`FieldPath`]. Uses [`salsa::Id`] instead of [`FieldName`] +/// to keep the type `'static`, allowing `FieldPath` to be salsa-interned. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum PathSegment { + Field(salsa::Id), Index(usize), } /// A path into a record structure, e.g. `[Field("alerts"), Index(0), Field("trigger")]`. /// -/// Not salsa-interned because it contains `FieldName<'db>` values that carry a -/// non-`'static` lifetime. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct FieldPath<'db>(pub Vec>); +/// Salsa-interned for O(1) cloning — paths are stored in every [`super::types::Blame`] +/// value, so cheap clones matter. +#[salsa::interned] +pub struct FieldPath<'db> { + #[returns(ref)] + pub segments: Vec, +} + +impl fmt::Debug for FieldPath<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let id: salsa::Id = salsa::plumbing::AsId::as_id(self); + f.debug_tuple("FieldPath").field(&id).finish() + } +} impl<'db> FieldPath<'db> { - pub fn root() -> Self { - Self(Vec::new()) + pub fn root(db: &'db dyn crate::Db) -> Self { + FieldPath::new(db, Vec::new()) } - pub fn push(&self, segment: PathSegment<'db>) -> Self { - let mut segments = self.0.clone(); - segments.push(segment); - Self(segments) + pub fn field(&self, db: &'db dyn crate::Db, name: FieldName<'db>) -> Self { + self.push(db, PathSegment::Field(salsa::plumbing::AsId::as_id(&name))) } - pub fn field(&self, name: FieldName<'db>) -> Self { - self.push(PathSegment::Field(name)) + pub fn index(&self, db: &'db dyn crate::Db, i: usize) -> Self { + self.push(db, PathSegment::Index(i)) } - pub fn index(&self, i: usize) -> Self { - self.push(PathSegment::Index(i)) + fn push(&self, db: &'db dyn crate::Db, segment: PathSegment) -> Self { + let segs = self.segments(db); + let mut new = Vec::with_capacity(segs.len() + 1); + new.extend_from_slice(segs); + new.push(segment); + FieldPath::new(db, new) } } diff --git a/crates/gnomon-db/src/eval/lower.rs b/crates/gnomon-db/src/eval/lower.rs index 7680d8d..a980b02 100644 --- a/crates/gnomon-db/src/eval/lower.rs +++ b/crates/gnomon-db/src/eval/lower.rs @@ -107,7 +107,7 @@ impl<'db> LowerCtx<'db> { let name = name_tok.text().to_string(); let decl_id = self.make_decl_id(0, DeclKind::Expr); let value = match binding.value_expr() { - Some(expr) => self.lower_top_expr(&expr, decl_id, &FieldPath::root()), + Some(expr) => self.lower_top_expr(&expr, decl_id, &FieldPath::root(self.db)), None => Value::Undefined, }; self.env.push((name, value)); @@ -131,7 +131,7 @@ impl<'db> LowerCtx<'db> { .enumerate() .map(|(i, expr)| { let decl_id = self.make_decl_id(i, decl_kind_for_expr(expr)); - let value = self.lower_top_expr(expr, decl_id, &FieldPath::root()); + let value = self.lower_top_expr(expr, decl_id, &FieldPath::root(self.db)); Blamed { value, blame: self.root_blame(decl_id), @@ -142,13 +142,13 @@ impl<'db> LowerCtx<'db> { } else { // Single expression mode let decl_id = self.make_decl_id(0, DeclKind::Expr); - self.lower_top_expr(&exprs[0], decl_id, &FieldPath::root()) + self.lower_top_expr(&exprs[0], decl_id, &FieldPath::root(self.db)) } } } fn lower_event(&mut self, ev: &ast::EventExpr, decl_id: DeclId<'db>) -> Record<'db> { - let base_path = FieldPath::root(); + let base_path = FieldPath::root(self.db); if ev.name().is_some() { // r[impl decl.short-event.desugar+2] @@ -173,7 +173,7 @@ impl<'db> LowerCtx<'db> { self.insert_field(&mut record, "start", value, decl_id, &base_path); } if let Some(dur_token) = span.duration() { - let blame = self.make_blame(decl_id, &base_path.field(self.intern("duration"))); + let blame = self.make_blame(decl_id, &base_path.field(self.db,self.intern("duration"))); if let Some(value) = desugar::desugar_duration(self.db, dur_token.text(), &blame) { @@ -210,7 +210,7 @@ impl<'db> LowerCtx<'db> { } fn lower_task(&mut self, task: &ast::TaskExpr, decl_id: DeclId<'db>) -> Record<'db> { - let base_path = FieldPath::root(); + let base_path = FieldPath::root(self.db); if task.name().is_some() { // r[impl decl.short-task.desugar+2] @@ -277,7 +277,7 @@ impl<'db> LowerCtx<'db> { }; let field_name = self.intern(name_token.text()); - let field_path = base_path.field(field_name); + let field_path = base_path.field(self.db,field_name); let blame = self.make_blame(decl_id, &field_path); let value = self.lower_top_expr(&value_expr, decl_id, &field_path); @@ -556,7 +556,7 @@ impl<'db> LowerCtx<'db> { ) -> Value<'db> { let mut items = Vec::new(); for (i, elem) in list.elements().enumerate() { - let elem_path = base_path.index(i); + let elem_path = base_path.index(self.db,i); let blame = self.make_blame(decl_id, &elem_path); let value = self.lower_top_expr(&elem, decl_id, &elem_path); items.push(Blamed { value, blame }); @@ -571,7 +571,7 @@ impl<'db> LowerCtx<'db> { base_path: &FieldPath<'db>, field_name: &str, ) -> Option> { - let blame = self.make_blame(decl_id, &base_path.field(self.intern(field_name))); + let blame = self.make_blame(decl_id, &base_path.field(self.db,self.intern(field_name))); if let Some(datetime_token) = dt.datetime() { desugar::desugar_datetime(self.db, datetime_token.text(), &blame) } else { @@ -783,7 +783,7 @@ impl<'db> LowerCtx<'db> { } ImportFormat::ICalendar => { let decl_id = self.make_decl_id(0, DeclKind::Expr); - let blame = self.make_blame(decl_id, &FieldPath::root()); + let blame = self.make_blame(decl_id, &FieldPath::root(self.db)); match super::import::translate_icalendar(self.db, &content, &blame) { Ok(value) => value, Err(msg) => { @@ -794,7 +794,7 @@ impl<'db> LowerCtx<'db> { } ImportFormat::JSCalendar => { let decl_id = self.make_decl_id(0, DeclKind::Expr); - let blame = self.make_blame(decl_id, &FieldPath::root()); + let blame = self.make_blame(decl_id, &FieldPath::root(self.db)); match super::import::translate_jscalendar(self.db, &content, &blame) { Ok(value) => value, Err(msg) => { @@ -820,7 +820,7 @@ impl<'db> LowerCtx<'db> { fn root_blame(&self, decl_id: DeclId<'db>) -> Blame<'db> { Blame { decl: decl_id, - path: FieldPath::root(), + path: FieldPath::root(self.db), } } @@ -840,7 +840,7 @@ impl<'db> LowerCtx<'db> { base_path: &FieldPath<'db>, ) { let field_name = self.intern(name); - let blame = self.make_blame(decl_id, &base_path.field(field_name)); + let blame = self.make_blame(decl_id, &base_path.field(self.db,field_name)); record.insert(self.db, field_name, Blamed { value, blame }); } diff --git a/crates/gnomon-db/src/eval/merge.rs b/crates/gnomon-db/src/eval/merge.rs index ee082ff..f818c82 100644 --- a/crates/gnomon-db/src/eval/merge.rs +++ b/crates/gnomon-db/src/eval/merge.rs @@ -289,7 +289,7 @@ fn flatten_to_records<'db>( let default_blame = || super::types::Blame { decl: DeclId::new(db, source, 0, DeclKind::Calendar), - path: FieldPath::root(), + path: FieldPath::root(db), }; match value { diff --git a/crates/gnomon-db/src/eval/render.rs b/crates/gnomon-db/src/eval/render.rs index 3787190..99471d8 100644 --- a/crates/gnomon-db/src/eval/render.rs +++ b/crates/gnomon-db/src/eval/render.rs @@ -111,10 +111,13 @@ impl<'db> RenderWithDb<'db> for DeclId<'db> { } } -impl<'db> RenderWithDb<'db> for PathSegment<'db> { +impl<'db> RenderWithDb<'db> for PathSegment { fn render_fmt(&self, f: &mut fmt::Formatter<'_>, db: &'db dyn Db) -> fmt::Result { match self { - PathSegment::Field(name) => name.render_fmt(f, db), + PathSegment::Field(id) => { + let name: FieldName<'db> = salsa::plumbing::FromId::from_id(*id); + name.render_fmt(f, db) + } PathSegment::Index(i) => write!(f, "[{i}]"), } } @@ -122,7 +125,7 @@ impl<'db> RenderWithDb<'db> for PathSegment<'db> { impl<'db> RenderWithDb<'db> for FieldPath<'db> { fn render_fmt(&self, f: &mut fmt::Formatter<'_>, db: &'db dyn Db) -> fmt::Result { - for (i, segment) in self.0.iter().enumerate() { + for (i, segment) in self.segments(db).iter().enumerate() { if i > 0 && matches!(segment, PathSegment::Field(_)) { write!(f, ".")?; } diff --git a/crates/gnomon-db/src/eval/rrule.rs b/crates/gnomon-db/src/eval/rrule.rs index 781d91d..08b72fd 100644 --- a/crates/gnomon-db/src/eval/rrule.rs +++ b/crates/gnomon-db/src/eval/rrule.rs @@ -441,7 +441,7 @@ mod tests { let decl_id = DeclId::new(db, source, 0, DeclKind::Event); Blame { decl: decl_id, - path: FieldPath::root(), + path: FieldPath::root(db), } } diff --git a/crates/gnomon-db/src/eval/types.rs b/crates/gnomon-db/src/eval/types.rs index 004a821..d7b9518 100644 --- a/crates/gnomon-db/src/eval/types.rs +++ b/crates/gnomon-db/src/eval/types.rs @@ -1,6 +1,6 @@ use super::interned::{DeclId, FieldName, FieldPath}; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Blame<'db> { pub decl: DeclId<'db>, pub path: FieldPath<'db>, @@ -131,7 +131,7 @@ mod tests { let source = SourceFile::new(db, "/test".into(), String::new()); Blame { decl: DeclId::new(db, source, 0, DeclKind::Expr), - path: FieldPath::root(), + path: FieldPath::root(db), } } From cd79e46639086388bbd532fcf4b92cc60078bcd1 Mon Sep 17 00:00:00 2001 From: Oliver Wooding Date: Sat, 14 Mar 2026 16:17:29 +0100 Subject: [PATCH 2/3] fix clippy clone_on_copy and formatting Blame and FieldPath are now Copy, so replace .clone() calls with copies or field shorthand. Co-Authored-By: Claude Opus 4.6 --- crates/gnomon-db/src/eval/desugar.rs | 6 +++--- crates/gnomon-db/src/eval/export.rs | 14 +++++++------- crates/gnomon-db/src/eval/import.rs | 4 ++-- crates/gnomon-db/src/eval/lower.rs | 13 +++++++------ crates/gnomon-db/src/eval/merge.rs | 6 +++--- crates/gnomon-db/src/eval/rrule.rs | 2 +- crates/gnomon-db/src/eval/types.rs | 10 +++++----- 7 files changed, 28 insertions(+), 27 deletions(-) diff --git a/crates/gnomon-db/src/eval/desugar.rs b/crates/gnomon-db/src/eval/desugar.rs index d98524e..8b9d5bb 100644 --- a/crates/gnomon-db/src/eval/desugar.rs +++ b/crates/gnomon-db/src/eval/desugar.rs @@ -141,7 +141,7 @@ pub fn desugar_every<'db>( "by_year_day", Value::List(vec![Blamed { value: Value::Integer(year_day), - blame: blame.clone(), + blame: *blame, }]), )); } @@ -156,7 +156,7 @@ pub fn desugar_every<'db>( "by_day", Value::List(vec![Blamed { value: Value::Record(nday_record), - blame: blame.clone(), + blame: *blame, }]), )); } else { @@ -199,7 +199,7 @@ pub(super) fn make_record<'db>( field_name, Blamed { value: value.clone(), - blame: blame.clone(), + blame: *blame, }, ); } diff --git a/crates/gnomon-db/src/eval/export.rs b/crates/gnomon-db/src/eval/export.rs index 0af55f9..72825d0 100644 --- a/crates/gnomon-db/src/eval/export.rs +++ b/crates/gnomon-db/src/eval/export.rs @@ -88,7 +88,7 @@ mod tests { FieldName::new(&db, "name".to_string()), Blamed { value: Value::String("hello".to_string()), - blame: blame.clone(), + blame, }, ); record.insert( @@ -96,7 +96,7 @@ mod tests { FieldName::new(&db, "count".to_string()), Blamed { value: Value::Integer(42), - blame: blame.clone(), + blame, }, ); @@ -129,7 +129,7 @@ mod tests { FieldName::new(&db, "uid".to_string()), Blamed { value: Value::String("cal-1".to_string()), - blame: blame.clone(), + blame, }, ); props.insert( @@ -137,7 +137,7 @@ mod tests { FieldName::new(&db, "type".to_string()), Blamed { value: Value::String("calendar".to_string()), - blame: blame.clone(), + blame, }, ); @@ -147,7 +147,7 @@ mod tests { FieldName::new(&db, "type".to_string()), Blamed { value: Value::String("event".to_string()), - blame: blame.clone(), + blame, }, ); entry.insert( @@ -155,7 +155,7 @@ mod tests { FieldName::new(&db, "title".to_string()), Blamed { value: Value::String("Standup".to_string()), - blame: blame.clone(), + blame, }, ); @@ -163,7 +163,7 @@ mod tests { properties: props, entries: vec![Blamed { value: entry, - blame: blame.clone(), + blame, }], foreign_import: false, }; diff --git a/crates/gnomon-db/src/eval/import.rs b/crates/gnomon-db/src/eval/import.rs index d90cfba..bbccbbf 100644 --- a/crates/gnomon-db/src/eval/import.rs +++ b/crates/gnomon-db/src/eval/import.rs @@ -24,7 +24,7 @@ fn import_value_to_value<'db>( .into_iter() .map(|v| Blamed { value: import_value_to_value(db, v, blame), - blame: blame.clone(), + blame: *blame, }) .collect(); Value::List(blamed_items) @@ -46,7 +46,7 @@ fn import_record_to_record<'db>( field_name, Blamed { value: import_value_to_value(db, val, blame), - blame: blame.clone(), + blame: *blame, }, ); } diff --git a/crates/gnomon-db/src/eval/lower.rs b/crates/gnomon-db/src/eval/lower.rs index a980b02..54bcc01 100644 --- a/crates/gnomon-db/src/eval/lower.rs +++ b/crates/gnomon-db/src/eval/lower.rs @@ -173,7 +173,8 @@ impl<'db> LowerCtx<'db> { self.insert_field(&mut record, "start", value, decl_id, &base_path); } if let Some(dur_token) = span.duration() { - let blame = self.make_blame(decl_id, &base_path.field(self.db,self.intern("duration"))); + let blame = self + .make_blame(decl_id, &base_path.field(self.db, self.intern("duration"))); if let Some(value) = desugar::desugar_duration(self.db, dur_token.text(), &blame) { @@ -277,7 +278,7 @@ impl<'db> LowerCtx<'db> { }; let field_name = self.intern(name_token.text()); - let field_path = base_path.field(self.db,field_name); + let field_path = base_path.field(self.db, field_name); let blame = self.make_blame(decl_id, &field_path); let value = self.lower_top_expr(&value_expr, decl_id, &field_path); @@ -556,7 +557,7 @@ impl<'db> LowerCtx<'db> { ) -> Value<'db> { let mut items = Vec::new(); for (i, elem) in list.elements().enumerate() { - let elem_path = base_path.index(self.db,i); + let elem_path = base_path.index(self.db, i); let blame = self.make_blame(decl_id, &elem_path); let value = self.lower_top_expr(&elem, decl_id, &elem_path); items.push(Blamed { value, blame }); @@ -571,7 +572,7 @@ impl<'db> LowerCtx<'db> { base_path: &FieldPath<'db>, field_name: &str, ) -> Option> { - let blame = self.make_blame(decl_id, &base_path.field(self.db,self.intern(field_name))); + let blame = self.make_blame(decl_id, &base_path.field(self.db, self.intern(field_name))); if let Some(datetime_token) = dt.datetime() { desugar::desugar_datetime(self.db, datetime_token.text(), &blame) } else { @@ -827,7 +828,7 @@ impl<'db> LowerCtx<'db> { fn make_blame(&self, decl_id: DeclId<'db>, path: &FieldPath<'db>) -> Blame<'db> { Blame { decl: decl_id, - path: path.clone(), + path: *path, } } @@ -840,7 +841,7 @@ impl<'db> LowerCtx<'db> { base_path: &FieldPath<'db>, ) { let field_name = self.intern(name); - let blame = self.make_blame(decl_id, &base_path.field(self.db,field_name)); + let blame = self.make_blame(decl_id, &base_path.field(self.db, field_name)); record.insert(self.db, field_name, Blamed { value, blame }); } diff --git a/crates/gnomon-db/src/eval/merge.rs b/crates/gnomon-db/src/eval/merge.rs index f818c82..3edc100 100644 --- a/crates/gnomon-db/src/eval/merge.rs +++ b/crates/gnomon-db/src/eval/merge.rs @@ -105,7 +105,7 @@ pub fn validate_calendar<'db>( ); calendar.entries.push(Blamed { value: r.clone(), - blame: item.blame.clone(), + blame: item.blame, }); } } @@ -241,7 +241,7 @@ fn derive_uids<'db>( uid_key, Blamed { value: Value::String(derived.to_string()), - blame: entry.blame.clone(), + blame: entry.blame, }, ); } @@ -297,7 +297,7 @@ fn flatten_to_records<'db>( let blame = r .values() .next() - .map(|b| b.blame.clone()) + .map(|b| b.blame) .unwrap_or_else(default_blame); vec![(r, blame)] } diff --git a/crates/gnomon-db/src/eval/rrule.rs b/crates/gnomon-db/src/eval/rrule.rs index 08b72fd..e64fa8d 100644 --- a/crates/gnomon-db/src/eval/rrule.rs +++ b/crates/gnomon-db/src/eval/rrule.rs @@ -524,7 +524,7 @@ mod tests { "by_day", Value::List(vec![Blamed { value: Value::Record(nday_record), - blame: blame.clone(), + blame, }]), ), ]; diff --git a/crates/gnomon-db/src/eval/types.rs b/crates/gnomon-db/src/eval/types.rs index d7b9518..911f595 100644 --- a/crates/gnomon-db/src/eval/types.rs +++ b/crates/gnomon-db/src/eval/types.rs @@ -149,7 +149,7 @@ mod tests { FieldName::new(&db, "zebra".to_string()), Blamed { value: Value::Integer(3), - blame: blame.clone(), + blame, }, ); record.insert( @@ -157,7 +157,7 @@ mod tests { FieldName::new(&db, "apple".to_string()), Blamed { value: Value::Integer(1), - blame: blame.clone(), + blame, }, ); record.insert( @@ -165,7 +165,7 @@ mod tests { FieldName::new(&db, "mango".to_string()), Blamed { value: Value::Integer(2), - blame: blame.clone(), + blame, }, ); @@ -187,7 +187,7 @@ mod tests { name, Blamed { value: Value::Integer(1), - blame: blame.clone(), + blame, }, ); record.insert( @@ -195,7 +195,7 @@ mod tests { name, Blamed { value: Value::Integer(2), - blame: blame.clone(), + blame, }, ); From 610b8cae5982c3d54cb0814915a8bd6f6a6b67d2 Mon Sep 17 00:00:00 2001 From: Oliver Wooding Date: Sat, 14 Mar 2026 16:19:43 +0100 Subject: [PATCH 3/3] fix collapsible_if clippy lint in iCalendar import Co-Authored-By: Claude Opus 4.6 --- crates/gnomon-import/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/gnomon-import/src/lib.rs b/crates/gnomon-import/src/lib.rs index a7da468..00d93b2 100644 --- a/crates/gnomon-import/src/lib.rs +++ b/crates/gnomon-import/src/lib.rs @@ -407,13 +407,13 @@ fn translate_ical_event(event: &calico::model::component::Event) -> ImportRecord if let (Some(dtstart), Some(dtend)) = (event.dtstart(), event.dtend()) { let start_tz = dtstart.params.tz_id(); let end_tz = dtend.params.tz_id(); - if let Some(etz) = end_tz { - if start_tz.map(|s| s.as_str()) != Some(etz.as_str()) { - fields.push(( - "end_time_zone", - ImportValue::String(etz.as_str().to_string()), - )); - } + if let Some(etz) = end_tz + && start_tz.map(|s| s.as_str()) != Some(etz.as_str()) + { + fields.push(( + "end_time_zone", + ImportValue::String(etz.as_str().to_string()), + )); } }