mirror of https://github.com/kdl-org/kdl-rs.git
feat(v1): add rudimentary, optional, KDL v1 parsing (#104)
This is kinda rough right now, since it involves actually pulling in the old kdl v1. Not ideal, but workable as a stepping stone while a real v1 parser gets written.
This commit is contained in:
parent
683e87a142
commit
6a7248c405
|
|
@ -8,18 +8,21 @@ readme = "README.md"
|
|||
homepage = "https://kdl.dev"
|
||||
repository = "https://github.com/kdl-org/kdl-rs"
|
||||
keywords = ["kdl", "document", "serialization", "config"]
|
||||
rust-version = "1.56.0"
|
||||
rust-version = "1.70.0"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["span"]
|
||||
default = ["span", "v1"]
|
||||
span = []
|
||||
v1-fallback = ["v1"]
|
||||
v1 = ["kdlv1"]
|
||||
|
||||
[dependencies]
|
||||
miette = "7.2.0"
|
||||
num = "0.4.2"
|
||||
thiserror = "1.0.40"
|
||||
winnow = { version = "0.6.20", features = ["alloc", "unstable-recover"] }
|
||||
kdlv1 = { package = "kdl", version = "4.7.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
miette = { version = "7.2.0", features = ["fancy"] }
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
msrv = "1.56.0"
|
||||
msrv = "1.70.0"
|
||||
|
|
|
|||
|
|
@ -334,13 +334,53 @@ impl KdlDocument {
|
|||
// .query_all(query)?
|
||||
// .filter_map(move |node| node.get(key.clone())))
|
||||
// }
|
||||
|
||||
/// Parses a string into a document.
|
||||
///
|
||||
/// If the `v1-fallback` feature is enabled, this method will first try to
|
||||
/// parse the string as a KDL v2 document, and, if that fails, it will try
|
||||
/// to parse again as a KDL v1 document. If both fail, only the v2 parse
|
||||
/// errors will be returned.
|
||||
pub fn parse(s: &str) -> Result<Self, KdlParseFailure> {
|
||||
#[cfg(not(feature = "v1-fallback"))]
|
||||
{
|
||||
crate::v2_parser::try_parse(crate::v2_parser::document, s)
|
||||
}
|
||||
#[cfg(feature = "v1-fallback")]
|
||||
{
|
||||
crate::v2_parser::try_parse(crate::v2_parser::document, s)
|
||||
.or_else(|e| KdlDocument::parse_v1(s).map_err(|_| e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a KDL v1 string into a document.
|
||||
#[cfg(feature = "v1")]
|
||||
pub fn parse_v1(s: &str) -> Result<Self, KdlParseFailure> {
|
||||
let ret: Result<kdlv1::KdlDocument, kdlv1::KdlError> = s.parse();
|
||||
ret.map(|x| x.into()).map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
impl From<kdlv1::KdlDocument> for KdlDocument {
|
||||
fn from(value: kdlv1::KdlDocument) -> Self {
|
||||
KdlDocument {
|
||||
nodes: value.nodes().iter().map(|x| x.clone().into()).collect(),
|
||||
format: Some(KdlDocumentFormat {
|
||||
leading: value.leading().unwrap_or("").into(),
|
||||
trailing: value.trailing().unwrap_or("").into(),
|
||||
}),
|
||||
#[cfg(feature = "span")]
|
||||
span: SourceSpan::new(value.span().offset().into(), value.span().len()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for KdlDocument {
|
||||
type Err = KdlParseFailure;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
crate::v2_parser::try_parse(crate::v2_parser::document, s)
|
||||
KdlDocument::parse(s)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
46
src/entry.rs
46
src/entry.rs
|
|
@ -178,6 +178,50 @@ impl KdlEntry {
|
|||
name.autoformat();
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a string into a entry.
|
||||
///
|
||||
/// If the `v1-fallback` feature is enabled, this method will first try to
|
||||
/// parse the string as a KDL v2 entry, and, if that fails, it will try
|
||||
/// to parse again as a KDL v1 entry. If both fail, only the v2 parse
|
||||
/// errors will be returned.
|
||||
pub fn parse(s: &str) -> Result<Self, KdlParseFailure> {
|
||||
#[cfg(not(feature = "v1-fallback"))]
|
||||
{
|
||||
v2_parser::try_parse(v2_parser::padded_node_entry, s)
|
||||
}
|
||||
#[cfg(feature = "v1-fallback")]
|
||||
{
|
||||
v2_parser::try_parse(v2_parser::padded_node_entry, s)
|
||||
.or_else(|e| KdlEntry::parse_v1(s).map_err(|_| e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a KDL v1 string into an entry.
|
||||
#[cfg(feature = "v1")]
|
||||
pub fn parse_v1(s: &str) -> Result<Self, KdlParseFailure> {
|
||||
let ret: Result<kdlv1::KdlEntry, kdlv1::KdlError> = s.parse();
|
||||
ret.map(|x| x.into()).map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
impl From<kdlv1::KdlEntry> for KdlEntry {
|
||||
fn from(value: kdlv1::KdlEntry) -> Self {
|
||||
KdlEntry {
|
||||
ty: value.ty().map(|x| x.clone().into()),
|
||||
value: value.value().clone().into(),
|
||||
name: value.name().map(|x| x.clone().into()),
|
||||
format: Some(KdlEntryFormat {
|
||||
value_repr: value.value_repr().unwrap_or("").into(),
|
||||
leading: value.leading().unwrap_or("").into(),
|
||||
trailing: value.trailing().unwrap_or("").into(),
|
||||
..Default::default()
|
||||
}),
|
||||
#[cfg(feature = "span")]
|
||||
span: SourceSpan::new(value.span().offset().into(), value.span().len()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for KdlEntry {
|
||||
|
|
@ -249,7 +293,7 @@ impl FromStr for KdlEntry {
|
|||
type Err = KdlParseFailure;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
v2_parser::try_parse(v2_parser::padded_node_entry, s)
|
||||
KdlEntry::parse(s)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
18
src/error.rs
18
src/error.rs
|
|
@ -74,3 +74,21 @@ pub struct KdlDiagnostic {
|
|||
#[diagnostic(severity)]
|
||||
pub severity: miette::Severity,
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
impl From<kdlv1::KdlError> for KdlParseFailure {
|
||||
fn from(value: kdlv1::KdlError) -> Self {
|
||||
let input = Arc::new(value.input);
|
||||
KdlParseFailure {
|
||||
input: input.clone(),
|
||||
diagnostics: vec![KdlDiagnostic {
|
||||
input,
|
||||
span: SourceSpan::new(value.span.offset().into(), value.span.len()),
|
||||
message: Some(format!("{}", value.kind)),
|
||||
label: value.label.map(|x| x.into()),
|
||||
help: value.help.map(|x| x.into()),
|
||||
severity: miette::Severity::Error,
|
||||
}],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,43 @@ impl KdlIdentifier {
|
|||
pub fn autoformat(&mut self) {
|
||||
self.repr = None;
|
||||
}
|
||||
|
||||
/// Parses a string into a entry.
|
||||
///
|
||||
/// If the `v1-fallback` feature is enabled, this method will first try to
|
||||
/// parse the string as a KDL v2 entry, and, if that fails, it will try
|
||||
/// to parse again as a KDL v1 entry. If both fail, only the v2 parse
|
||||
/// errors will be returned.
|
||||
pub fn parse(s: &str) -> Result<Self, KdlParseFailure> {
|
||||
#[cfg(not(feature = "v1-fallback"))]
|
||||
{
|
||||
v2_parser::try_parse(v2_parser::identifier, s)
|
||||
}
|
||||
#[cfg(feature = "v1-fallback")]
|
||||
{
|
||||
v2_parser::try_parse(v2_parser::identifier, s)
|
||||
.or_else(|e| KdlIdentifier::parse_v1(s).map_err(|_| e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a KDL v1 string into an entry.
|
||||
#[cfg(feature = "v1")]
|
||||
pub fn parse_v1(s: &str) -> Result<Self, KdlParseFailure> {
|
||||
let ret: Result<kdlv1::KdlIdentifier, kdlv1::KdlError> = s.parse();
|
||||
ret.map(|x| x.into()).map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
impl From<kdlv1::KdlIdentifier> for KdlIdentifier {
|
||||
fn from(value: kdlv1::KdlIdentifier) -> Self {
|
||||
KdlIdentifier {
|
||||
value: value.value().into(),
|
||||
repr: value.repr().map(|x| x.into()),
|
||||
#[cfg(feature = "span")]
|
||||
span: SourceSpan::new(value.span().offset().into(), value.span().len()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for KdlIdentifier {
|
||||
|
|
@ -131,7 +168,7 @@ impl FromStr for KdlIdentifier {
|
|||
type Err = KdlParseFailure;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
v2_parser::try_parse(v2_parser::identifier, s)
|
||||
KdlIdentifier::parse(s)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
49
src/node.rs
49
src/node.rs
|
|
@ -325,6 +325,55 @@ impl KdlNode {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a string into a node.
|
||||
///
|
||||
/// If the `v1-fallback` feature is enabled, this method will first try to
|
||||
/// parse the string as a KDL v2 node, and, if that fails, it will try
|
||||
/// to parse again as a KDL v1 node. If both fail, only the v2 parse
|
||||
/// errors will be returned.
|
||||
pub fn parse(s: &str) -> Result<Self, KdlParseFailure> {
|
||||
#[cfg(not(feature = "v1-fallback"))]
|
||||
{
|
||||
v2_parser::try_parse(v2_parser::padded_node, s)
|
||||
}
|
||||
#[cfg(feature = "v1-fallback")]
|
||||
{
|
||||
v2_parser::try_parse(v2_parser::padded_node, s)
|
||||
.or_else(|e| KdlNode::parse_v1(s).map_err(|_| e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a KDL v1 string into a document.
|
||||
#[cfg(feature = "v1")]
|
||||
pub fn parse_v1(s: &str) -> Result<Self, KdlParseFailure> {
|
||||
let ret: Result<kdlv1::KdlNode, kdlv1::KdlError> = s.parse();
|
||||
ret.map(|x| x.into()).map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
impl From<kdlv1::KdlNode> for KdlNode {
|
||||
fn from(value: kdlv1::KdlNode) -> Self {
|
||||
KdlNode {
|
||||
ty: value.ty().map(|x| x.clone().into()),
|
||||
name: value.name().clone().into(),
|
||||
entries: value.entries().iter().map(|x| x.clone().into()).collect(),
|
||||
children: value.children().map(|x| x.clone().into()),
|
||||
format: Some(KdlNodeFormat {
|
||||
leading: value.leading().unwrap_or("").into(),
|
||||
before_ty_name: "".into(),
|
||||
after_ty_name: "".into(),
|
||||
after_ty: "".into(),
|
||||
before_children: value.before_children().unwrap_or("").into(),
|
||||
before_terminator: "".into(),
|
||||
terminator: "".into(),
|
||||
trailing: value.trailing().unwrap_or("").into(),
|
||||
}),
|
||||
#[cfg(feature = "span")]
|
||||
span: SourceSpan::new(value.span().offset().into(), value.span().len()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Query language
|
||||
|
|
|
|||
17
src/value.rs
17
src/value.rs
|
|
@ -261,6 +261,23 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
impl From<kdlv1::KdlValue> for KdlValue {
|
||||
fn from(value: kdlv1::KdlValue) -> Self {
|
||||
match value {
|
||||
kdlv1::KdlValue::RawString(s) => KdlValue::String(s),
|
||||
kdlv1::KdlValue::String(s) => KdlValue::String(s),
|
||||
kdlv1::KdlValue::Base2(i) => KdlValue::Integer(i.into()),
|
||||
kdlv1::KdlValue::Base8(i) => KdlValue::Integer(i.into()),
|
||||
kdlv1::KdlValue::Base10(i) => KdlValue::Integer(i.into()),
|
||||
kdlv1::KdlValue::Base10Float(f) => KdlValue::Float(f),
|
||||
kdlv1::KdlValue::Base16(i) => KdlValue::Integer(i.into()),
|
||||
kdlv1::KdlValue::Bool(b) => KdlValue::Bool(b),
|
||||
kdlv1::KdlValue::Null => KdlValue::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
|
|
|||
Loading…
Reference in New Issue