1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
//! Traits used for the library
//!
//! Most users will never implement these manually. See
//! [`Decode`](derive@crate::Decode)` and
//! [`DecodeScalar`](derive@crate::DecodeScalar) for a
//! documentation of the derives to implement these traits.
use std::fmt;
use crate::ast::{SpannedNode, Literal, Value, TypeName};
use crate::span::Spanned;
use crate::errors::DecodeError;
use crate::decode::Context;
/// Trait to decode KDL node from the AST
pub trait Decode<S: ErrorSpan>: Sized {
/// Decodes the node from the ast
fn decode_node(node: &SpannedNode<S>, ctx: &mut Context<S>)
-> Result<Self, DecodeError<S>>;
}
/// Trait to decode children of the KDL node, mostly used for root document
pub trait DecodeChildren<S: ErrorSpan>: Sized {
/// Decodes from a list of chidren ASTs
fn decode_children(nodes: &[SpannedNode<S>], ctx: &mut Context<S>)
-> Result<Self, DecodeError<S>>;
}
/// The trait is implemented for structures that can be used as part of other
/// structs
///
/// The type of field that `#[knuffel(flatten)]` is used for should implement
/// this trait. It is automatically implemented by `#[derive(knuffel::Decode)]`
/// by structures that have only optional properties and children (no
/// arguments).
pub trait DecodePartial<S: ErrorSpan>: Sized {
/// The method is called when unknown child is encountered by parent
/// structure
///
/// Returns `Ok(true)` if the child is "consumed" (i.e. stored in this
/// structure).
fn insert_child(&mut self, node: &SpannedNode<S>, ctx: &mut Context<S>)
-> Result<bool, DecodeError<S>>;
/// The method is called when unknown property is encountered by parent
/// structure
///
/// Returns `Ok(true)` if the property is "consumed" (i.e. stored in this
/// structure).
fn insert_property(&mut self,
name: &Spanned<Box<str>, S>, value: &Value<S>,
ctx: &mut Context<S>)
-> Result<bool, DecodeError<S>>;
}
/// The trait that decodes scalar value and checks its type
pub trait DecodeScalar<S: ErrorSpan>: Sized {
/// Typecheck the value
///
/// This method can only emit errors to the context in type mismatch case.
/// Errors emitted to the context are considered fatal once the whole data
/// is processed but non fatal when encountered. So even if there is a type
/// in type name we can proceed and try parsing actual value.
fn type_check(type_name: &Option<Spanned<TypeName, S>>,
ctx: &mut Context<S>);
/// Decode value without typecheck
///
/// This can be used by wrappers to parse some know value but use a
/// different typename (kinda emulated subclassing)
fn raw_decode(value: &Spanned<Literal, S>, ctx: &mut Context<S>)
-> Result<Self, DecodeError<S>>;
/// Decode the value and typecheck
///
/// This should not be overriden and uses `type_check` in combination with
/// `raw_decode`.
fn decode(value: &Value<S>, ctx: &mut Context<S>)
-> Result<Self, DecodeError<S>>
{
Self::type_check(&value.type_name, ctx);
Self::raw_decode(&value.literal, ctx)
}
}
/// The trait that decodes span into the final structure
pub trait DecodeSpan<S: ErrorSpan>: Sized {
/// Decode span
///
/// This method can use some extra data (say file name) from the context.
/// Although, by default context is empty and end users are expected to use
/// [`parse_with_context`](crate::parse_with_context) to add some values.
fn decode_span(span: &S, ctx: &mut Context<S>) -> Self;
}
impl<T: ErrorSpan> DecodeSpan<T> for T {
fn decode_span(span: &T, _: &mut Context<T>) -> Self {
span.clone()
}
}
/// Span must implement this trait to be used in the error messages
///
/// Custom span types can be used for this unlike for [`Span`]
pub trait ErrorSpan: Into<miette::SourceSpan>
+ Clone + fmt::Debug + Send + Sync + 'static {}
impl<T> ErrorSpan for T
where T: Into<miette::SourceSpan>,
T: Clone + fmt::Debug + Send + Sync + 'static,
{}
/// Span trait used for parsing source code
///
/// It's sealed because needs some tight interoperation with the parser. Use
/// [`DecodeSpan`] to convert spans whenever needed.
pub trait Span: sealed::Sealed + chumsky::Span + ErrorSpan {}
#[allow(missing_debug_implementations)]
pub(crate) mod sealed {
pub type Stream<'a, S, T> = chumsky::Stream<
'a, char, S, Map<std::str::Chars<'a>, T>
>;
pub struct Map<I, F>(pub(crate) I, pub(crate) F);
pub trait SpanTracker {
type Span;
fn next_span(&mut self, c: char) -> Self::Span;
}
impl<I, T> Iterator for Map<I, T>
where I: Iterator<Item=char>,
T: SpanTracker,
{
type Item = (char, T::Span);
fn next(&mut self) -> Option<(char, T::Span)> {
self.0.next().map(|c| (c, self.1.next_span(c)))
}
}
pub trait Sealed {
type Tracker: SpanTracker<Span=Self>;
/// Note assuming ascii, single-width, non-newline chars here
fn at_start(&self, chars: usize) -> Self;
fn at_end(&self) -> Self;
/// Note assuming ascii, single-width, non-newline chars here
fn before_start(&self, chars: usize) -> Self;
fn length(&self) -> usize;
fn stream(s: &str) -> Stream<'_, Self, Self::Tracker>
where Self: chumsky::Span;
}
}