Skip to content

Commit f8e66db

Browse files
authored
Merge pull request #2773 from ehuss/remove-public-toml
Remove toml as a public dependency
2 parents fe76ee6 + 529cfc3 commit f8e66db

File tree

10 files changed

+179
-238
lines changed

10 files changed

+179
-238
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/mdbook-core/src/config.rs

Lines changed: 59 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -24,32 +24,32 @@
2424
//! [build]
2525
//! src = "out"
2626
//!
27-
//! [other-table.foo]
27+
//! [preprocessor.my-preprocessor]
2828
//! bar = 123
2929
//! "#;
3030
//!
3131
//! // load the `Config` from a toml string
3232
//! let mut cfg = Config::from_str(src)?;
3333
//!
3434
//! // retrieve a nested value
35-
//! let bar = cfg.get("other-table.foo.bar").cloned();
36-
//! assert_eq!(bar, Some(Value::Integer(123)));
35+
//! let bar = cfg.get::<i32>("preprocessor.my-preprocessor.bar")?;
36+
//! assert_eq!(bar, Some(123));
3737
//!
3838
//! // Set the `output.html.theme` directory
39-
//! assert!(cfg.get("output.html").is_none());
39+
//! assert!(cfg.get::<Value>("output.html")?.is_none());
4040
//! cfg.set("output.html.theme", "./themes");
4141
//!
4242
//! // then load it again, automatically deserializing to a `PathBuf`.
43-
//! let got: Option<PathBuf> = cfg.get_deserialized_opt("output.html.theme")?;
43+
//! let got = cfg.get("output.html.theme")?;
4444
//! assert_eq!(got, Some(PathBuf::from("./themes")));
4545
//! # Ok(())
4646
//! # }
4747
//! # run().unwrap()
4848
//! ```
4949
50+
use crate::utils::TomlExt;
5051
use crate::utils::log_backtrace;
51-
use crate::utils::toml_ext::TomlExt;
52-
use anyhow::{Context, Error, Result, bail};
52+
use anyhow::{Context, Error, Result};
5353
use log::{debug, trace, warn};
5454
use serde::{Deserialize, Deserializer, Serialize, Serializer};
5555
use std::collections::HashMap;
@@ -154,18 +154,28 @@ impl Config {
154154
}
155155
}
156156

157-
/// Fetch an arbitrary item from the `Config` as a `toml::Value`.
157+
/// Get a value from the configuration.
158158
///
159-
/// You can use dotted indices to access nested items (e.g.
160-
/// `output.html.playground` will fetch the "playground" out of the html output
161-
/// table).
162-
pub fn get(&self, key: &str) -> Option<&Value> {
163-
self.rest.read(key)
164-
}
165-
166-
/// Fetch a value from the `Config` so you can mutate it.
167-
pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
168-
self.rest.read_mut(key)
159+
/// This fetches a value from the book configuration. The key can have
160+
/// dotted indices to access nested items (e.g. `output.html.playground`
161+
/// will fetch the "playground" out of the html output table).
162+
///
163+
/// This does not have access to the [`Config::book`], [`Config::build`],
164+
/// or [`Config::rust`] fields.
165+
///
166+
/// Returns `Ok(None)` if the field is not set.
167+
///
168+
/// Returns `Err` if it fails to deserialize.
169+
pub fn get<'de, T: Deserialize<'de>>(&self, name: &str) -> Result<Option<T>> {
170+
self.rest
171+
.read(name)
172+
.map(|value| {
173+
value
174+
.clone()
175+
.try_into()
176+
.with_context(|| "Couldn't deserialize the value")
177+
})
178+
.transpose()
169179
}
170180

171181
/// Convenience method for getting the html renderer's configuration.
@@ -177,7 +187,7 @@ impl Config {
177187
#[doc(hidden)]
178188
pub fn html_config(&self) -> Option<HtmlConfig> {
179189
match self
180-
.get_deserialized_opt("output.html")
190+
.get("output.html")
181191
.with_context(|| "Parsing configuration [output.html]")
182192
{
183193
Ok(Some(config)) => Some(config),
@@ -189,35 +199,12 @@ impl Config {
189199
}
190200
}
191201

192-
/// Deprecated, use get_deserialized_opt instead.
193-
#[deprecated = "use get_deserialized_opt instead"]
194-
pub fn get_deserialized<'de, T: Deserialize<'de>, S: AsRef<str>>(&self, name: S) -> Result<T> {
195-
let name = name.as_ref();
196-
match self.get_deserialized_opt(name)? {
197-
Some(value) => Ok(value),
198-
None => bail!("Key not found, {:?}", name),
199-
}
200-
}
201-
202-
/// Convenience function to fetch a value from the config and deserialize it
203-
/// into some arbitrary type.
204-
pub fn get_deserialized_opt<'de, T: Deserialize<'de>, S: AsRef<str>>(
205-
&self,
206-
name: S,
207-
) -> Result<Option<T>> {
208-
let name = name.as_ref();
209-
self.get(name)
210-
.map(|value| {
211-
value
212-
.clone()
213-
.try_into()
214-
.with_context(|| "Couldn't deserialize the value")
215-
})
216-
.transpose()
217-
}
218-
219202
/// Set a config key, clobbering any existing values along the way.
220203
///
204+
/// The key can have dotted indices for nested items (e.g.
205+
/// `output.html.playground` will set the "playground" in the html output
206+
/// table).
207+
///
221208
/// The only way this can fail is if we can't serialize `value` into a
222209
/// `toml::Value`.
223210
pub fn set<S: Serialize, I: AsRef<str>>(&mut self, index: I, value: S) -> Result<()> {
@@ -230,25 +217,15 @@ impl Config {
230217
self.book.update_value(key, value);
231218
} else if let Some(key) = index.strip_prefix("build.") {
232219
self.build.update_value(key, value);
220+
} else if let Some(key) = index.strip_prefix("rust.") {
221+
self.rust.update_value(key, value);
233222
} else {
234223
self.rest.insert(index, value);
235224
}
236225

237226
Ok(())
238227
}
239228

240-
/// Get the table associated with a particular renderer.
241-
pub fn get_renderer<I: AsRef<str>>(&self, index: I) -> Option<&Table> {
242-
let key = format!("output.{}", index.as_ref());
243-
self.get(&key).and_then(Value::as_table)
244-
}
245-
246-
/// Get the table associated with a particular preprocessor.
247-
pub fn get_preprocessor<I: AsRef<str>>(&self, index: I) -> Option<&Table> {
248-
let key = format!("preprocessor.{}", index.as_ref());
249-
self.get(&key).and_then(Value::as_table)
250-
}
251-
252229
fn from_legacy(mut table: Value) -> Config {
253230
let mut cfg = Config::default();
254231

@@ -1019,32 +996,16 @@ mod tests {
1019996
};
1020997

1021998
let cfg = Config::from_str(src).unwrap();
1022-
let got: RandomOutput = cfg.get_deserialized_opt("output.random").unwrap().unwrap();
999+
let got: RandomOutput = cfg.get("output.random").unwrap().unwrap();
10231000

10241001
assert_eq!(got, should_be);
10251002

1026-
let got_baz: Vec<bool> = cfg
1027-
.get_deserialized_opt("output.random.baz")
1028-
.unwrap()
1029-
.unwrap();
1003+
let got_baz: Vec<bool> = cfg.get("output.random.baz").unwrap().unwrap();
10301004
let baz_should_be = vec![true, true, false];
10311005

10321006
assert_eq!(got_baz, baz_should_be);
10331007
}
10341008

1035-
#[test]
1036-
fn mutate_some_stuff() {
1037-
// really this is just a sanity check to make sure the borrow checker
1038-
// is happy...
1039-
let src = COMPLEX_CONFIG;
1040-
let mut config = Config::from_str(src).unwrap();
1041-
let key = "output.html.playground.editable";
1042-
1043-
assert_eq!(config.get(key).unwrap(), &Value::Boolean(true));
1044-
*config.get_mut(key).unwrap() = Value::Boolean(false);
1045-
assert_eq!(config.get(key).unwrap(), &Value::Boolean(false));
1046-
}
1047-
10481009
/// The config file format has slightly changed (metadata stuff is now under
10491010
/// the `book` table instead of being at the top level) so we're adding a
10501011
/// **temporary** compatibility check. You should be able to still load the
@@ -1104,13 +1065,29 @@ mod tests {
11041065
let key = "foo.bar.baz";
11051066
let value = "Something Interesting";
11061067

1107-
assert!(cfg.get(key).is_none());
1068+
assert!(cfg.get::<i32>(key).unwrap().is_none());
11081069
cfg.set(key, value).unwrap();
11091070

1110-
let got: String = cfg.get_deserialized_opt(key).unwrap().unwrap();
1071+
let got: String = cfg.get(key).unwrap().unwrap();
11111072
assert_eq!(got, value);
11121073
}
11131074

1075+
#[test]
1076+
fn set_special_tables() {
1077+
let mut cfg = Config::default();
1078+
assert_eq!(cfg.book.title, None);
1079+
cfg.set("book.title", "my title").unwrap();
1080+
assert_eq!(cfg.book.title, Some("my title".to_string()));
1081+
1082+
assert_eq!(&cfg.build.build_dir, Path::new("book"));
1083+
cfg.set("build.build-dir", "some-directory").unwrap();
1084+
assert_eq!(&cfg.build.build_dir, Path::new("some-directory"));
1085+
1086+
assert_eq!(cfg.rust.edition, None);
1087+
cfg.set("rust.edition", "2024").unwrap();
1088+
assert_eq!(cfg.rust.edition, Some(RustEdition::E2024));
1089+
}
1090+
11141091
#[test]
11151092
fn parse_env_vars() {
11161093
let inputs = vec![
@@ -1141,18 +1118,15 @@ mod tests {
11411118
let key = "foo.bar";
11421119
let value = "baz";
11431120

1144-
assert!(cfg.get(key).is_none());
1121+
assert!(cfg.get::<String>(key).unwrap().is_none());
11451122

11461123
let encoded_key = encode_env_var(key);
11471124
// TODO: This is unsafe, and should be rewritten to use a process.
11481125
unsafe { env::set_var(encoded_key, value) };
11491126

11501127
cfg.update_from_env();
11511128

1152-
assert_eq!(
1153-
cfg.get_deserialized_opt::<String, _>(key).unwrap().unwrap(),
1154-
value
1155-
);
1129+
assert_eq!(cfg.get::<String>(key).unwrap().unwrap(), value);
11561130
}
11571131

11581132
#[test]
@@ -1162,20 +1136,15 @@ mod tests {
11621136
let value = json!({"array": [1, 2, 3], "number": 13.37});
11631137
let value_str = serde_json::to_string(&value).unwrap();
11641138

1165-
assert!(cfg.get(key).is_none());
1139+
assert!(cfg.get::<serde_json::Value>(key).unwrap().is_none());
11661140

11671141
let encoded_key = encode_env_var(key);
11681142
// TODO: This is unsafe, and should be rewritten to use a process.
11691143
unsafe { env::set_var(encoded_key, value_str) };
11701144

11711145
cfg.update_from_env();
11721146

1173-
assert_eq!(
1174-
cfg.get_deserialized_opt::<serde_json::Value, _>(key)
1175-
.unwrap()
1176-
.unwrap(),
1177-
value
1178-
);
1147+
assert_eq!(cfg.get::<serde_json::Value>(key).unwrap().unwrap(), value);
11791148
}
11801149

11811150
#[test]

crates/mdbook-core/src/utils/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use std::sync::LazyLock;
99

1010
pub mod fs;
1111
mod string;
12-
pub mod toml_ext;
12+
mod toml_ext;
13+
14+
pub(crate) use self::toml_ext::TomlExt;
1315

1416
pub use self::string::{
1517
take_anchored_lines, take_lines, take_rustdoc_include_anchored_lines,

crates/mdbook-core/src/utils/toml_ext.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@
33
use toml::value::{Table, Value};
44

55
/// Helper for working with toml types.
6-
pub trait TomlExt {
6+
pub(crate) trait TomlExt {
77
/// Read a dotted key.
88
fn read(&self, key: &str) -> Option<&Value>;
9-
/// Read a dotted key for a mutable value.
10-
fn read_mut(&mut self, key: &str) -> Option<&mut Value>;
119
/// Insert with a dotted key.
1210
fn insert(&mut self, key: &str, value: Value);
1311
/// Delete a dotted key value.
@@ -23,14 +21,6 @@ impl TomlExt for Value {
2321
}
2422
}
2523

26-
fn read_mut(&mut self, key: &str) -> Option<&mut Value> {
27-
if let Some((head, tail)) = split(key) {
28-
self.get_mut(head)?.read_mut(tail)
29-
} else {
30-
self.get_mut(key)
31-
}
32-
}
33-
3424
fn insert(&mut self, key: &str, value: Value) {
3525
if !self.is_table() {
3626
*self = Value::Table(Table::new());

crates/mdbook-driver/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mdbook-preprocessor.workspace = true
1717
mdbook-renderer.workspace = true
1818
mdbook-summary.workspace = true
1919
regex.workspace = true
20+
serde.workspace = true
2021
serde_json.workspace = true
2122
shlex.workspace = true
2223
tempfile.workspace = true

crates/mdbook-driver/src/builtin_renderers/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use std::fs;
1010
use std::io::{self, ErrorKind};
1111
use std::path::{Path, PathBuf};
1212
use std::process::{Command, Stdio};
13-
use toml::Value;
1413

1514
pub use self::markdown_renderer::MarkdownRenderer;
1615

@@ -108,8 +107,9 @@ impl CmdRenderer {
108107
let optional_key = format!("output.{}.optional", self.name);
109108

110109
let is_optional = match ctx.config.get(&optional_key) {
111-
Some(Value::Boolean(value)) => *value,
112-
_ => false,
110+
Ok(Some(value)) => value,
111+
Err(e) => bail!("expected bool for `{optional_key}`: {e}"),
112+
Ok(None) => false,
113113
};
114114

115115
if is_optional {

0 commit comments

Comments
 (0)