boml/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(missing_docs)]
3
4mod parser;
5pub mod table;
6mod text;
7pub mod types;
8
9use {
10	crate::table::TomlTable,
11	std::{
12		fmt::{Debug, Display},
13		ops::Deref,
14	},
15	text::Span,
16};
17
18/// Attempts to parse the given TOML.
19pub fn parse(str: &str) -> Result<Toml<'_>, TomlError> {
20	parser::parse_str(str)
21}
22
23/// A parsed TOML file.
24#[derive(Debug)]
25pub struct Toml<'a> {
26	source: &'a str,
27	table: TomlTable<'a>,
28}
29impl<'a> Toml<'a> {
30	/// Attempts to parse the given TOML.
31	pub fn new(str: &'a str) -> Result<Self, TomlError<'a>> {
32		parser::parse_str(str)
33	}
34	/// Attempts to parse the given TOML.
35	pub fn parse(str: &'a str) -> Result<Self, TomlError<'a>> {
36		parser::parse_str(str)
37	}
38
39	/// The source code of this TOML.
40	pub fn source(&self) -> &str {
41		self.source
42	}
43}
44impl<'a> From<Toml<'a>> for TomlTable<'a> {
45	fn from(value: Toml<'a>) -> TomlTable<'a> {
46		value.table
47	}
48}
49impl<'a> Deref for Toml<'a> {
50	type Target = TomlTable<'a>;
51
52	fn deref(&self) -> &Self::Target {
53		&self.table
54	}
55}
56
57/// An error while parsing TOML.
58pub struct TomlError<'a> {
59	/// An excerpt of the region of text that caused the error.
60	pub src: Span<'a>,
61	/// The type of parsing error; see [`TomlErrorKind`].
62	pub kind: TomlErrorKind,
63}
64impl Debug for TomlError<'_> {
65	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66		let mut start = self.src.source[..self.src.start].bytes().enumerate().rev();
67		let mut newlines = 3u8;
68		while newlines != 0 {
69			match start.next() {
70				None => break,
71				Some((_, b'\n')) => newlines -= 1,
72				_ => {}
73			}
74		}
75		let start = start.next().map(|(idx, _)| idx + 2).unwrap_or(0);
76
77		let mut end = self.src.source[self.src.end..].bytes().enumerate();
78		let mut newlines = 3u8;
79		while newlines != 0 {
80			match end.next() {
81				None => break,
82				Some((_, b'\n')) => newlines -= 1,
83				_ => {}
84			}
85		}
86		let end = end
87			.next()
88			.map(|(idx, _)| self.src.end + idx - 1)
89			.unwrap_or(self.src.source.len());
90
91		write!(
92			f,
93			"Error: {:?} at `{}`\nIn:\n{}",
94			self.kind,
95			self.src.as_str(),
96			&self.src.source[start..end]
97		)
98	}
99}
100impl Display for TomlError<'_> {
101	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102		write!(f, "{self:?}")
103	}
104}
105/// A type of error while parsing TOML.
106#[derive(Debug, PartialEq, Eq)]
107pub enum TomlErrorKind {
108	/// A bare key (key without quotes) contains an invalid character.
109	InvalidBareKey,
110	/// There was a space in the middle of a bare key.
111	BareKeyHasSpace,
112	/// There was no `=` sign in a key/value assignment.
113	NoEqualsInAssignment,
114	/// There was no key in a key/value assignment.
115	NoKeyInAssignment,
116	/// There was no value in a key/value assignment.
117	NoValueInAssignment,
118	/// A basic string (`"hello"`) didn't have a closing quote.
119	UnclosedBasicString,
120	/// A literal string (`'hello'`) didn't have a closing quote.
121	UnclosedLiteralString,
122	/// A quoted key didn't have a closing quote.
123	UnclosedQuotedKey,
124	/// The value in a key/value assignment wasn't recognised.
125	UnrecognisedValue,
126	/// The same key was used twice.
127	ReusedKey,
128	/// A number was too big to fit in an i64. This will be thrown for both
129	/// positive and negative numbers.
130	NumberTooLarge,
131	/// An integer has an invalid base. Valid bases are hex (0x), octal (0o),
132	/// and binary (0b).
133	NumberHasInvalidBase,
134	/// A literal number starts with a 0.
135	NumberHasLeadingZero,
136	/// A number is malformed/not parseable.
137	InvalidNumber,
138	/// A basic string has an unknown escape sequence.
139	UnknownEscapeSequence,
140	/// A unicode escape in a basic string has an unknown unicode scalar value.
141	UnknownUnicodeScalar,
142	/// A table (`[table]`) had an unclosed bracket.
143	UnclosedTableBracket,
144	/// An inline table (`{key = "val", one = 2}`) had an unclosed bracket.
145	UnclosedInlineTableBracket,
146	/// An array of tables (`[[array_table]]`) was missing closing brackets.
147	UnclosedArrayOfTablesBracket,
148	/// An array literal (`[true, "hi", 123]`) was missing a closing bracket.
149	UnclosedArrayBracket,
150	/// There was no `,` in between values in an inline table or array.
151	NoCommaDelimeter,
152	/// One section (year, month, day, hour, etc) of a date/time value had too
153	/// many digits.
154	DateTimeTooManyDigits,
155	/// A date value was missing its month.
156	DateMissingMonth,
157	/// A date value was missing its day.
158	DateMissingDay,
159	/// A date value was missing the `-` between a year/month/day.
160	DateMissingDash,
161	/// A time value was missing its minute.
162	TimeMissingMinute,
163	/// A time value was missing its second.
164	TimeMissingSecond,
165	/// A time value was missing the `:` between its hour/minute/second.
166	TimeMissingColon,
167	/// The offset portion of an offset datetime was missing its hour.
168	OffsetMissingHour,
169	/// The offset portion of an offset datetime was missing its minute.
170	OffsetMissingMinute,
171}
172
173/// Types that may be useful to have imported while using BOML.
174pub mod prelude {
175	pub use crate::{
176		table::{TomlGetError, TomlTable},
177		types::{TomlValue, TomlValueType},
178		Toml, TomlError, TomlErrorKind,
179	};
180}