config: add basic abstraction over &str, [&str], and &ConfigNamePathBuf

We usually look up config objects with a static name, but commands like "jj
config get" has to parse a user input as a config key. We could consolidate the
name type to &ConfigNamePathBuf, but it would be inconvenient if all callers had
to do config.get(&"..".parse()?). It's also undesirable to pass a raw user input
in to .get() as a string.

I originally considered making it support fallible conversion, but doing that
would complicate error handling path. Not all functions should return an error.
It's better to do fallible parsing at the call sites instead.

A string config name is parsed into a temporary ConfigNamePathBuf object. If the
allocation cost matters, the output path type can be abstracted.
This commit is contained in:
Yuya Nishihara 2024-11-28 17:45:28 +09:00
parent 5946ef3880
commit 8557a0ff0a

View file

@ -14,6 +14,7 @@
//! Configuration store helpers.
use std::borrow::Borrow;
use std::borrow::Cow;
use std::fmt;
use std::ops::Range;
@ -138,6 +139,73 @@ impl fmt::Display for ConfigNamePathBuf {
}
}
/// Value that can be converted to a dotted config name path.
///
/// This is an abstraction to specify a config name path in either a string or a
/// parsed form. It's similar to `Into<T>`, but the output type `T` is
/// constrained by the source type.
pub trait ToConfigNamePath: Sized {
/// Path type to be converted from `Self`.
type Output: Borrow<ConfigNamePathBuf>;
/// Converts this object into a dotted config name path.
fn into_name_path(self) -> Self::Output;
}
impl ToConfigNamePath for ConfigNamePathBuf {
type Output = Self;
fn into_name_path(self) -> Self::Output {
self
}
}
impl ToConfigNamePath for &ConfigNamePathBuf {
type Output = Self;
fn into_name_path(self) -> Self::Output {
self
}
}
impl ToConfigNamePath for &'static str {
// This can be changed to ConfigNamePathStr(str) if allocation cost matters.
type Output = ConfigNamePathBuf;
/// Parses this string into a dotted config name path.
///
/// The string must be a valid TOML dotted key. A static str is required to
/// prevent API misuse.
fn into_name_path(self) -> Self::Output {
self.parse()
.expect("valid TOML dotted key must be provided")
}
}
impl<const N: usize> ToConfigNamePath for [&str; N] {
type Output = ConfigNamePathBuf;
fn into_name_path(self) -> Self::Output {
self.into_iter().collect()
}
}
impl<const N: usize> ToConfigNamePath for &[&str; N] {
type Output = ConfigNamePathBuf;
fn into_name_path(self) -> Self::Output {
self.as_slice().into_name_path()
}
}
impl ToConfigNamePath for &[&str] {
type Output = ConfigNamePathBuf;
fn into_name_path(self) -> Self::Output {
self.iter().copied().collect()
}
}
/// Source of configuration variables in order of precedence.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum ConfigSource {