use std::borrow::Cow;
use std::collections::HashMap;
use std::hash::Hash;
use oneyaml_core::{Diagnostic, DuplicateKeys, Label, Span, code};
use super::merge::MergeAcc;
use crate::error::{Error, warning};
use crate::value::Value;
pub(crate) fn key_text(key: &Value) -> String {
use crate::error::{MAX_SEGMENT_CHARS, segment, short_chars};
use crate::value::walk::KeyDebug;
if let Value::Tagged(_) = key {
let text = short_chars(format_args!("{}", KeyDebug(key)), MAX_SEGMENT_CHARS);
if !text.starts_with("Tagged(") {
return text;
}
}
match key.untag_ref() {
Value::String(s) => segment(s),
Value::Null => "null".to_owned(),
Value::Bool(b) => b.to_string(),
Value::Number(n) => short_chars(format_args!("{n}"), MAX_SEGMENT_CHARS),
other => short_chars(format_args!("{}", KeyDebug(other)), 40),
}
}
pub(crate) fn on_duplicate(
policy: DuplicateKeys,
key: &str,
first: Span,
dup: Span,
) -> Result<Diagnostic, Error> {
let first_label = Label::new(first, "first defined here");
match policy {
DuplicateKeys::LastWins => Ok(warning(
code::DUPLICATE_KEY_LAST_WINS,
format!("duplicate key `{key}`: the last value wins"),
dup,
)
.with_secondary(first_label)),
DuplicateKeys::Error => Err(Error::new(
code::DUPLICATE_KEY,
format!("duplicate key `{key}`"),
Some(dup),
)
.with_secondary(first_label)
.with_help(
"remove one of the entries, or use DuplicateKeys::LastWins \
(Compat::serde_yaml()) to keep the last value",
)),
_ => {
debug_assert!(false, "no duplicate-key handling for {policy:?}");
Err(Error::internal("unknown duplicate-key policy", Some(dup)))
}
}
}
pub(crate) fn duplicate_at(
warnings: &mut crate::diag_cap::Capped,
policy: DuplicateKeys,
replayed: bool,
key: impl FnOnce() -> String,
first: Span,
dup: Span,
) -> Result<(), Error> {
if replayed && policy == DuplicateKeys::LastWins {
return Ok(());
}
warnings.duplicate(policy, key, first, dup)
}
pub(crate) fn invalid_merge(span: Span) -> Error {
Error::new(
code::INVALID_MERGE_VALUE,
"the value of a merge key `<<` must be a mapping or a sequence of mappings",
Some(span),
)
.with_help("turn Settings::merge_keys off to read `<<` as an ordinary key")
}
pub(crate) struct Merge<K, V> {
acc: Option<Box<MergeAcc<K, V>>>,
first: Option<Span>,
}
impl<K, V> Default for Merge<K, V> {
fn default() -> Self {
Self {
acc: None,
first: None,
}
}
}
impl<K: Hash + Eq, V> Merge<K, V> {
pub(crate) fn key(
&mut self,
span: Span,
replayed: bool,
policy: DuplicateKeys,
warnings: &mut crate::diag_cap::Capped,
) -> Result<(), Error> {
if let Some(first) = self.first {
duplicate_at(warnings, policy, replayed, || "<<".to_owned(), first, span)?;
self.acc = None;
} else {
self.first = Some(span);
}
Ok(())
}
pub(crate) fn absorb(&mut self, source: Box<MergeAcc<K, V>>) {
match self.acc.as_mut() {
None => self.acc = Some(source),
Some(acc) => acc.absorb_later(*source),
}
}
pub(crate) fn is_empty(&self) -> bool {
self.first.is_none()
}
pub(crate) fn into_merged(self) -> Option<Box<MergeAcc<K, V>>> {
self.acc
}
pub(crate) fn finish(self, explicit: MergeAcc<K, V>) -> MergeAcc<K, V> {
match self.acc {
None => explicit,
Some(acc) => explicit.append_later(*acc),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) enum KeyRepr<'a> {
Str(Cow<'a, str>),
Other(Value),
}
impl KeyRepr<'_> {
pub(crate) fn of_value(v: Value) -> Self {
match v {
Value::String(s) => KeyRepr::Str(Cow::Owned(s)),
other => KeyRepr::Other(other),
}
}
pub(crate) fn into_value(self) -> Value {
match self {
KeyRepr::Str(s) => Value::String(s.into_owned()),
KeyRepr::Other(v) => v,
}
}
}
const SMALL_KEYS: usize = 8;
pub(crate) trait SmallKey {
fn is_small(&self) -> bool;
}
impl SmallKey for KeyRepr<'_> {
fn is_small(&self) -> bool {
match self {
KeyRepr::Str(_) => true,
KeyRepr::Other(v) => {
!matches!(v, Value::Sequence(_) | Value::Mapping(_) | Value::Tagged(_))
}
}
}
}
pub(crate) struct KeyMap<K, V> {
small: Vec<(K, V)>,
big: HashMap<K, V>,
spilled: bool,
}
impl<K, V> Default for KeyMap<K, V> {
fn default() -> Self {
Self {
small: Vec::new(),
big: HashMap::new(),
spilled: false,
}
}
}
impl<K: Eq + Hash + SmallKey, V> KeyMap<K, V> {
pub(crate) fn get(&self, key: &K) -> Option<&V> {
if self.spilled || !key.is_small() {
self.big.get(key)
} else {
self.small.iter().find(|(k, _)| k == key).map(|(_, v)| v)
}
}
pub(crate) fn contains_key(&self, key: &K) -> bool {
self.get(key).is_some()
}
pub(crate) fn insert_new(&mut self, key: K, value: V) {
if self.spilled || !key.is_small() {
self.big.insert(key, value);
} else if self.small.len() < SMALL_KEYS {
self.small.push((key, value));
} else {
self.big.reserve(SMALL_KEYS.saturating_add(1));
self.big.extend(self.small.drain(..));
self.big.insert(key, value);
self.spilled = true;
}
}
pub(crate) fn clear(&mut self) {
self.small.clear();
self.big.clear();
self.spilled = false;
}
pub(crate) fn drain(&mut self) -> impl Iterator<Item = (K, V)> + '_ {
self.spilled = false;
self.small.drain(..).chain(self.big.drain())
}
}
#[derive(Default)]
pub(crate) struct KeySet<'a> {
keys: KeyMap<KeyRepr<'a>, Span>,
}
impl<'a> KeySet<'a> {
pub(crate) fn insert(&mut self, key: KeyRepr<'a>, span: Span) -> Option<Span> {
match self.keys.get(&key) {
Some(first) => Some(*first),
None => {
self.keys.insert_new(key, span);
None
}
}
}
pub(crate) fn first(&self, key: &KeyRepr<'a>) -> Option<Span> {
self.keys.get(key).copied()
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing)]
use super::*;
type M = Merge<Value, Value>;
fn map(pairs: &[(&str, i32)]) -> MergeAcc<Value, Value> {
MergeAcc::from_entries(
pairs
.iter()
.map(|(k, v)| (Value::from(*k), Value::from(*v))),
)
}
fn done(m: M, explicit: &[(&str, i32)]) -> Vec<(Value, Value)> {
m.finish(map(explicit)).into_entries().collect()
}
#[test]
fn merge_left_to_right_explicit_wins() {
let mut m = M::default();
let mut w = crate::diag_cap::Capped::with_keep(usize::MAX);
m.key(Span::empty(0), false, DuplicateKeys::Error, &mut w)
.unwrap();
m.absorb(Box::new(map(&[("a", 1), ("b", 1)])));
m.absorb(Box::new(map(&[("b", 2), ("c", 2)])));
let out = done(m, &[("c", 3)]);
assert_eq!(
out,
vec![
(Value::from("c"), Value::from(3)),
(Value::from("a"), Value::from(1)),
(Value::from("b"), Value::from(1))
]
);
assert_eq!(w.total(), 0);
}
#[test]
fn merge_errors_and_repeats() {
assert_eq!(
invalid_merge(Span::new(1, 2)).code(),
code::INVALID_MERGE_VALUE
);
let mut w = crate::diag_cap::Capped::with_keep(usize::MAX);
let mut m = M::default();
assert!(m.is_empty());
m.key(Span::empty(0), false, DuplicateKeys::LastWins, &mut w)
.unwrap();
m.absorb(Box::new(map(&[("a", 1)])));
m.key(Span::empty(5), false, DuplicateKeys::LastWins, &mut w)
.unwrap();
m.absorb(Box::new(map(&[("b", 2)])));
let (kept, _) = w.take();
assert_eq!(kept[0].code, code::DUPLICATE_KEY_LAST_WINS);
assert_eq!(kept[0].secondary[0].message, "first defined here");
assert!(!m.is_empty());
assert_eq!(done(m, &[]), vec![(Value::from("b"), Value::from(2))]);
let mut m = M::default();
m.key(Span::empty(0), false, DuplicateKeys::Error, &mut w)
.unwrap();
let e = m
.key(Span::empty(3), false, DuplicateKeys::Error, &mut w)
.unwrap_err();
assert_eq!(e.code(), code::DUPLICATE_KEY);
}
#[test]
fn key_set_and_text() {
let mut s = KeySet::default();
assert_eq!(s.insert(KeyRepr::Str("a".into()), Span::new(0, 1)), None);
assert_eq!(
s.insert(KeyRepr::of_value(Value::from("a")), Span::new(5, 6)),
Some(Span::new(0, 1))
);
assert_eq!(
s.insert(KeyRepr::of_value(Value::from(1)), Span::new(7, 8)),
None
);
assert_eq!(key_text(&Value::Null), "null");
assert_eq!(key_text(&Value::from(true)), "true");
assert_eq!(key_text(&Value::from(1.5)), "1.5");
let long = Value::from(vec![Value::from("x".repeat(80))]);
assert!(key_text(&long).ends_with('…'));
}
#[test]
fn collection_and_tagged_keys_are_hashed_from_the_start() {
let seq = |i: i64| KeyRepr::of_value(Value::from(vec![Value::from(i)]));
let tagged = |t: &str| {
KeyRepr::of_value(Value::Tagged(Box::new(crate::value::TaggedValue {
tag: crate::value::Tag::new("!t"),
value: Value::from(t),
})))
};
assert!(!seq(0).is_small() && !tagged("x").is_small());
assert!(KeyRepr::Str("x".into()).is_small());
assert!(KeyRepr::of_value(Value::Null).is_small());
let mut m: KeyMap<KeyRepr<'_>, usize> = KeyMap::default();
for i in 0..20 {
m.insert_new(seq(i), 0);
}
m.insert_new(tagged("x"), 1);
m.insert_new(KeyRepr::Str("x".into()), 2);
assert_eq!((m.small.len(), m.big.len(), m.spilled), (1, 21, false));
assert_eq!(m.get(&seq(19)), Some(&0));
assert_eq!(m.get(&tagged("x")), Some(&1));
assert_eq!(m.get(&KeyRepr::Str("x".into())), Some(&2));
assert!(!m.contains_key(&tagged("y")) && !m.contains_key(&seq(20)));
for i in 0..SMALL_KEYS {
m.insert_new(KeyRepr::of_value(Value::from(i as i64)), 3);
}
assert!(m.spilled && m.small.is_empty());
assert_eq!(m.get(&KeyRepr::Str("x".into())), Some(&2));
assert_eq!(m.get(&seq(3)), Some(&0));
assert_eq!(m.drain().count(), 22 + SMALL_KEYS);
assert!(!m.spilled);
m.insert_new(KeyRepr::Str("y".into()), 4);
assert_eq!(m.small.len(), 1);
}
#[test]
fn key_map_finds_the_same_duplicates_below_and_past_the_linear_bound() {
for n in [0usize, 1, SMALL_KEYS - 1, SMALL_KEYS, SMALL_KEYS + 1, 40] {
let mut m: KeyMap<KeyRepr<'_>, usize> = KeyMap::default();
for i in 0..n {
let k = KeyRepr::of_value(Value::from(i as i64));
assert!(!m.contains_key(&k), "{n} {i}");
m.insert_new(k, i);
}
for i in 0..n {
let k = KeyRepr::of_value(Value::from(i as i64));
assert_eq!(m.get(&k), Some(&i), "{n} {i}");
assert!(!m.contains_key(&KeyRepr::Str(i.to_string().into())));
}
let a = Value::Mapping(crate::value::Mapping::from_iter([
(Value::from("a"), Value::from(1)),
(Value::from("b"), Value::from(2)),
]));
let b = Value::Mapping(crate::value::Mapping::from_iter([
(Value::from("b"), Value::from(2)),
(Value::from("a"), Value::from(1)),
]));
m.insert_new(KeyRepr::of_value(a), 999);
assert_eq!(m.get(&KeyRepr::of_value(b)), Some(&999), "{n}");
let mut drained: Vec<usize> = m.drain().map(|(_, v)| v).collect();
drained.sort_unstable();
let mut want: Vec<usize> = (0..n).collect();
want.push(999);
assert_eq!(drained, want);
assert!(m.get(&KeyRepr::of_value(Value::from(0))).is_none());
m.insert_new(KeyRepr::Str("x".into()), 1);
m.clear();
assert!(!m.contains_key(&KeyRepr::Str("x".into())));
}
}
}