use std::borrow::Cow;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::Arc;
use oneyaml_core::Span;
use oneyaml_syntax::event::{TagForm, TagKey, TagSource};
use oneyaml_syntax::tree::Node as TreeNode;
use crate::document::tags::DocTags;
use crate::error::Error;
use crate::resolve::TagInput;
use crate::value::{Tag as ValueTag, TagPrefix, TagStr};
pub(crate) mod budget;
pub(crate) mod build;
pub(crate) mod expand;
pub(crate) mod feed;
pub(crate) mod keys;
pub(crate) mod merge;
pub(crate) mod scalar;
pub(crate) mod settings;
pub(crate) mod tree;
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum Tag<'a> {
Absent,
NonSpecific,
Named(TagText<'a>),
}
impl Tag<'_> {
pub(crate) fn input(&self) -> TagInput<'_> {
match self {
Tag::Absent => TagInput::Absent,
Tag::NonSpecific => TagInput::NonSpecific,
Tag::Named(t) => TagInput::Named(t.parts()),
}
}
pub(crate) fn value_tag(&self, name: TagStr<'_>) -> ValueTag {
match self {
Tag::Named(t) => t.value_tag(),
_ => ValueTag::new(name.to_owned_string()),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct TagText<'a>(ValueTag, PhantomData<&'a str>);
impl TagText<'_> {
pub(crate) fn new(t: ValueTag) -> Self {
Self(t, PhantomData)
}
pub(crate) fn parts(&self) -> TagStr<'_> {
self.0.parts()
}
pub(crate) fn tag(&self) -> &ValueTag {
&self.0
}
pub(crate) fn value_tag(&self) -> ValueTag {
self.0.clone()
}
}
impl PartialEq<str> for TagText<'_> {
fn eq(&self, other: &str) -> bool {
self.0 == *other
}
}
#[derive(Default)]
pub(crate) struct TagInterner {
prefixes: HashMap<(usize, u64), Vec<Arc<TagPrefix>>>,
seen: HashMap<(usize, usize), (ValueTag, ValueTag)>,
canonical: HashMap<(usize, u64), Vec<ValueTag>>,
starts: HashMap<(usize, usize), bool>,
}
impl TagInterner {
pub(crate) fn prefix(&mut self, text: Box<str>) -> Arc<TagPrefix> {
let p = TagPrefix::new(text);
let bucket = self
.prefixes
.entry((p.text().len(), p.digest()))
.or_default();
if let Some(same) = bucket.iter().find(|q| q.text() == p.text()) {
return Arc::clone(same);
}
let p = Arc::new(p);
bucket.push(Arc::clone(&p));
p
}
pub(crate) fn tag(&mut self, t: ValueTag) -> ValueTag {
let id = t.identity();
if let Some((_, c)) = self.seen.get(&id) {
return c.clone();
}
let key = (t.len(), t.digest());
let candidates = self.canonical.get(&key).cloned().unwrap_or_default();
let mut found = None;
for c in candidates {
if self.same_text(&c, &t) {
found = Some(c);
break;
}
}
let c = match found {
Some(c) => c,
None => {
self.canonical.entry(key).or_default().push(t.clone());
t.clone()
}
};
self.seen.insert(id, (t, c.clone()));
c
}
fn same_text(&mut self, a: &ValueTag, b: &ValueTag) -> bool {
if a.same_prefix(b) {
return a.suffix() == b.suffix();
}
let len = |t: &ValueTag| t.shared_prefix().map_or(0, |p| p.text().len());
let (a, b) = if len(a) <= len(b) { (a, b) } else { (b, a) };
let addr = |t: &ValueTag| t.shared_prefix().map_or(0, |p| Arc::as_ptr(p).addr());
let pre_a = a.shared_prefix().map_or("", |p| p.text());
let pre_b = b.shared_prefix().map_or("", |p| p.text());
let starts = *self
.starts
.entry((addr(a), addr(b)))
.or_insert_with(|| pre_b.starts_with(pre_a));
starts
&& a.suffix()
.strip_prefix(pre_b.get(pre_a.len()..).unwrap_or(""))
.is_some_and(|rest| rest == b.suffix())
}
}
#[derive(Default)]
pub(crate) struct TagCache<'a> {
seen: HashMap<TagKey<'a>, ValueTag>,
directives: HashMap<TagKey<'a>, (Arc<TagPrefix>, Box<[u8]>)>,
interner: TagInterner,
doc: Option<&'a DocTags>,
}
impl<'a> TagCache<'a> {
pub(crate) fn with_doc(doc: &'a DocTags) -> Self {
Self {
doc: Some(doc),
..Self::default()
}
}
pub(crate) fn node(&mut self, n: TreeNode<'a>) -> Result<Tag<'a>, Error> {
let source = n.tag_source();
match self.doc {
Some(doc) if source.as_ref().is_some_and(|t| t.prefix_key().is_some()) => {
Ok(match doc.try_tag(n)? {
Tag::Named(t) => Tag::Named(TagText::new(self.interner.tag(t.value_tag()))),
other => other,
})
}
_ => self.tag(source),
}
}
pub(crate) fn tag(&mut self, t: Option<TagSource<'a>>) -> Result<Tag<'a>, Error> {
let Some(t) = t else { return Ok(Tag::Absent) };
if t.form == TagForm::NonSpecific {
return Ok(Tag::NonSpecific);
}
let key = t.key();
if let Some(v) = self.seen.get(&key) {
return Ok(Tag::Named(TagText::new(v.clone())));
}
let prefix = match t.prefix_key() {
Some(pk) => {
let interner = &mut self.interner;
let entry = self.directives.entry(pk).or_insert_with(|| {
let bytes = t.directive_prefix().unwrap_or_default();
split_prefix(interner, &bytes)
});
Some((Arc::clone(&entry.0), &*entry.1))
}
None => None,
};
let Some(v) = build_tag(&t, prefix) else {
debug_assert!(false, "a tag property that does not resolve");
return Err(Error::internal(
"a tag property that does not resolve",
Some(t.span),
));
};
let v = self.interner.tag(v);
self.seen.insert(key, v.clone());
Ok(Tag::Named(TagText::new(v)))
}
pub(crate) fn one(t: Option<TagSource<'a>>) -> Result<Tag<'a>, Error> {
Self::default().tag(t)
}
}
fn split_prefix(interner: &mut TagInterner, bytes: &[u8]) -> (Arc<TagPrefix>, Box<[u8]>) {
let valid = match core::str::from_utf8(bytes) {
Ok(_) => bytes.len(),
Err(e) => e.valid_up_to(),
};
let (head, tail) = bytes.split_at(valid.min(bytes.len()));
let text = core::str::from_utf8(head).unwrap_or_default();
(interner.prefix(text.into()), tail.into())
}
fn build_tag(t: &TagSource<'_>, prefix: Option<(Arc<TagPrefix>, &[u8])>) -> Option<ValueTag> {
let suffix = t.decoded_suffix();
let (shared, lead): (Option<Arc<TagPrefix>>, &[u8]) = match prefix {
Some((p, carried)) => (Some(p), carried),
None => (None, t.default_prefix().unwrap_or_default().as_bytes()),
};
let text = if lead.is_empty() {
match suffix {
Cow::Borrowed(b) => Arc::from(core::str::from_utf8(b).ok()?),
Cow::Owned(b) => Arc::from(String::from_utf8(b).ok()?),
}
} else {
let mut joined = Vec::with_capacity(lead.len().saturating_add(suffix.len()));
joined.extend_from_slice(lead);
joined.extend_from_slice(&suffix);
Arc::from(String::from_utf8(joined).ok()?)
};
Some(ValueTag::from_parts(shared, text))
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum Node<'a> {
DocStart {
yaml11: bool,
},
DocEnd,
Scalar {
value: Cow<'a, str>,
plain: bool,
tag: Tag<'a>,
},
SeqStart {
tag: Tag<'a>,
},
SeqEnd,
MapStart {
tag: Tag<'a>,
},
MapEnd,
Alias {
name: &'a str,
},
StreamEnd,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Ev<'a> {
pub(crate) node: Node<'a>,
pub(crate) anchor: Option<&'a str>,
pub(crate) span: Span,
pub(crate) replayed: bool,
}
impl<'a> Ev<'a> {
pub(crate) fn new(node: Node<'a>, span: Span) -> Self {
Self {
node,
anchor: None,
span,
replayed: false,
}
}
pub(crate) fn starts_node(&self) -> bool {
matches!(
self.node,
Node::Scalar { .. }
| Node::SeqStart { .. }
| Node::MapStart { .. }
| Node::Alias { .. }
)
}
}
pub(crate) trait Source<'a> {
fn next_event(&mut self) -> Result<Ev<'a>, Error>;
fn take_warnings(&mut self) -> (Vec<oneyaml_core::Diagnostic>, crate::diag_cap::Dropped);
fn collect_warnings(&mut self, warnings: crate::diag_cap::Capped);
fn replay_last(&self) -> Option<tree::Replay<'a>> {
None
}
fn take_preload(&mut self) -> Result<Vec<Ev<'a>>, Error> {
Ok(Vec::new())
}
fn nodes_before(&mut self) -> Option<usize> {
None
}
fn document_start(&mut self) -> Option<usize> {
None
}
}
pub(crate) trait Pull<'a> {
fn next(&mut self) -> Result<Ev<'a>, Error>;
fn peek(&mut self) -> Result<&Ev<'a>, Error>;
fn last_end(&self) -> usize;
fn peeked_delivered_replay(&self) -> bool {
false
}
}
impl<'a, S: Source<'a>> Pull<'a> for expand::Expander<'a, S> {
fn next(&mut self) -> Result<Ev<'a>, Error> {
expand::Expander::next(self)
}
fn peek(&mut self) -> Result<&Ev<'a>, Error> {
expand::Expander::peek(self)
}
fn last_end(&self) -> usize {
expand::Expander::last_end(self)
}
fn peeked_delivered_replay(&self) -> bool {
self.starts_delivered_replay()
}
}
pub(crate) struct Recording<'p, 'a, P> {
inner: &'p mut P,
seen: Vec<Ev<'a>>,
}
impl<'p, 'a, P: Pull<'a>> Recording<'p, 'a, P> {
pub(crate) fn new(inner: &'p mut P, first: &Ev<'a>) -> Self {
Self {
inner,
seen: vec![first.clone()],
}
}
pub(crate) fn into_events(self) -> Vec<Ev<'a>> {
self.seen
}
}
impl<'a, P: Pull<'a>> Pull<'a> for Recording<'_, 'a, P> {
fn next(&mut self) -> Result<Ev<'a>, Error> {
let ev = self.inner.next()?;
self.seen.push(ev.clone());
Ok(ev)
}
fn peek(&mut self) -> Result<&Ev<'a>, Error> {
self.inner.peek()
}
fn last_end(&self) -> usize {
self.inner.last_end()
}
}