use std::sync::Arc;
use oneyaml_core::{Diagnostic, Settings};
use oneyaml_syntax::tree::SyntaxTree;
use serde::Serialize;
use super::{
AsKey, CommentId, CommentOwner, Document, EditError, IntoPath, NodeId, NodeMut, Parsed,
Removed, Shared, document_id,
};
use crate::error::Error;
pub struct Stream {
editor: Document,
ids: Vec<u64>,
guards: Vec<Option<super::edit::Guard>>,
}
impl Clone for Stream {
fn clone(&self) -> Self {
let ids: Vec<u64> = self.ids.iter().map(|_| document_id()).collect();
let mut editor = self.editor.clone();
if let Some(id) = editor.index.and_then(|i| ids.get(i)) {
editor.id = *id;
}
Self {
editor,
ids,
guards: self.guards.clone(),
}
}
}
impl core::fmt::Debug for Stream {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let s = self.shared();
f.debug_struct("Stream")
.field("documents", &s.doc_ids.len())
.field("span", &s.tree.root().span())
.field("edited", &s.edited)
.field("settings", &s.settings)
.finish_non_exhaustive()
}
}
impl Stream {
fn new(shared: Shared) -> Self {
let ids = shared.doc_ids.iter().map(|_| document_id()).collect();
let guards = shared.doc_ids.iter().map(|_| None).collect();
Self {
editor: Document::of_stream(&Arc::new(shared), None, None),
ids,
guards,
}
}
fn shared(&self) -> &Shared {
&self.editor.shared
}
fn strict(mut shared: Shared) -> Result<Self, Error> {
let found = shared.strict_check(false)?;
shared.add_diagnostics(found);
shared.mode = super::Mode::Strict { single: false };
Ok(Self::new(shared))
}
fn recovering(mut shared: Shared) -> Parsed<Self> {
shared.mode = super::Mode::Recovering { single: false };
let found = shared.recovering_check();
shared.add_diagnostics(found);
let diagnostics = shared.diagnostics.clone();
Parsed {
value: Self::new(shared),
diagnostics,
}
}
pub fn parse(src: impl Into<String>) -> Result<Self, Error> {
Self::parse_with(src, &Settings::new())
}
pub fn parse_with(src: impl Into<String>, settings: &Settings) -> Result<Self, Error> {
Self::strict(Shared::from_str(src.into(), settings))
}
pub fn parse_recovering(src: impl Into<String>) -> Parsed<Self> {
Self::parse_recovering_with(src, &Settings::new())
}
pub fn parse_recovering_with(src: impl Into<String>, settings: &Settings) -> Parsed<Self> {
Self::recovering(Shared::from_str(src.into(), settings))
}
pub fn from_slice(bytes: &[u8]) -> Result<Self, Error> {
Self::from_slice_with(bytes, &Settings::new())
}
pub fn from_slice_with(bytes: &[u8], settings: &Settings) -> Result<Self, Error> {
Self::strict(Shared::from_slice_strict(bytes, settings)?)
}
pub fn from_slice_recovering(bytes: &[u8], settings: &Settings) -> Parsed<Self> {
Self::recovering(Shared::from_slice(bytes, settings))
}
#[must_use]
pub fn len(&self) -> usize {
self.shared().doc_ids.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.shared().doc_ids.is_empty()
}
#[must_use]
pub fn document(&self, i: usize) -> Option<Document> {
let doc = *self.shared().doc_ids.get(i)?;
let mut d = Document::of_stream(&self.editor.shared, Some(doc), Some(i));
d.explicit.clone_from(&self.editor.explicit);
Some(d)
}
pub fn document_mut(&mut self, i: usize) -> Option<DocumentMut<'_>> {
let doc = *self.shared().doc_ids.get(i)?;
let id = *self.ids.get(i)?;
let e = &mut self.editor;
if e.index != Some(i) || e.doc != Some(doc) || e.id != id {
let mine = self.guards.get_mut(i).and_then(Option::take);
let theirs = std::mem::replace(&mut e.guard, mine);
if let Some(slot) = e.index.and_then(|j| self.guards.get_mut(j)) {
*slot = theirs;
}
e.doc = Some(doc);
e.index = Some(i);
e.id = id;
}
Some(DocumentMut { doc: e })
}
pub fn documents(&self) -> impl ExactSizeIterator<Item = Document> + '_ {
(0..self.len())
.filter_map(|i| self.document(i))
.collect::<Vec<_>>()
.into_iter()
}
#[must_use]
pub fn diagnostics(&self) -> Vec<Diagnostic> {
let s = self.shared();
if s.edited {
return s.edited_stream_diagnostics();
}
s.diagnostics.clone()
}
#[must_use]
pub fn has_errors(&self) -> bool {
let s = self.shared();
let error = |d: &Diagnostic| d.severity == oneyaml_core::Severity::Error;
if s.edited {
return s.edited_stream_diagnostics().iter().any(error);
}
s.settings_error() || !s.error_marks.is_empty() || !s.origin.invalid.is_empty()
}
#[must_use]
pub fn settings(&self) -> &Settings {
&self.shared().settings
}
#[must_use]
pub fn syntax(&self) -> &SyntaxTree {
&self.shared().tree
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
Document::of_stream(&self.editor.shared, None, None).to_bytes()
}
}
impl core::fmt::Display for Stream {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
for n in self.shared().tree.root().descendants() {
if n.is_token() {
f.write_str(&n.text())?;
}
}
Ok(())
}
}
#[derive(Debug)]
pub struct DocumentMut<'s> {
doc: &'s mut Document,
}
impl core::ops::Deref for DocumentMut<'_> {
type Target = Document;
fn deref(&self) -> &Document {
self.doc
}
}
impl DocumentMut<'_> {
pub fn node_mut(&mut self, id: NodeId) -> Result<NodeMut<'_>, EditError> {
self.doc.node_mut(id)
}
pub fn root_mut(&mut self) -> Result<NodeMut<'_>, EditError> {
self.doc.root_mut()
}
pub fn get_mut<K: AsKey>(&mut self, key: K) -> Result<NodeMut<'_>, EditError> {
self.doc.get_mut(key)
}
pub fn get_path_mut<P: IntoPath>(&mut self, path: P) -> Result<NodeMut<'_>, EditError> {
self.doc.get_path_mut(path)
}
pub fn set_path<P: IntoPath, T: Serialize>(
&mut self,
path: P,
value: T,
) -> Result<NodeMut<'_>, EditError> {
self.doc.set_path(path, value)
}
pub fn get_path_or_create<P: IntoPath>(&mut self, path: P) -> Result<NodeMut<'_>, EditError> {
self.doc.get_path_or_create(path)
}
pub fn set_root<T: Serialize>(&mut self, value: T) -> Result<NodeMut<'_>, EditError> {
self.doc.set_root(value)
}
pub fn remove_path<P: IntoPath>(&mut self, path: P) -> Result<Removed, EditError> {
self.doc.remove_path(path)
}
pub fn remove_node(&mut self, id: NodeId) -> Result<Removed, EditError> {
self.doc.remove_node(id)
}
pub fn remove_keep_comments(&mut self, id: NodeId) -> Result<Removed, EditError> {
self.doc.remove_keep_comments(id)
}
pub fn set_leading<I>(&mut self, id: NodeId, lines: I) -> Result<Vec<CommentId>, EditError>
where
I: IntoIterator,
I::Item: AsRef<str>,
{
self.doc.set_leading(id, lines)
}
pub fn set_trailing(
&mut self,
id: NodeId,
text: Option<&str>,
) -> Result<Option<CommentId>, EditError> {
self.doc.set_trailing(id, text)
}
pub fn clear_comments(&mut self, id: NodeId) -> Result<Vec<String>, EditError> {
self.doc.clear_comments(id)
}
pub fn set_blank_lines_before(&mut self, id: NodeId, n: usize) -> Result<(), EditError> {
self.doc.set_blank_lines_before(id, n)
}
pub fn set_comment_blank_lines(&mut self, id: CommentId, n: usize) -> Result<(), EditError> {
self.doc.set_comment_blank_lines(id, n)
}
pub fn set_header<I>(&mut self, lines: I) -> Result<Vec<CommentId>, EditError>
where
I: IntoIterator,
I::Item: AsRef<str>,
{
self.doc.set_header(lines)
}
pub fn set_footer<I>(&mut self, lines: I) -> Result<Vec<CommentId>, EditError>
where
I: IntoIterator,
I::Item: AsRef<str>,
{
self.doc.set_footer(lines)
}
pub fn attach_comment(&mut self, id: CommentId, owner: CommentOwner) -> Result<(), EditError> {
self.doc.attach_comment(id, owner)
}
pub fn detach_comment(&mut self, id: CommentId) -> Result<Option<CommentOwner>, EditError> {
self.doc.detach_comment(id)
}
}