Skip to content

Commit 514408d

Browse files
committed
WIP: Implement custom components and logger
1 parent 6bd7398 commit 514408d

File tree

12 files changed

+564
-237
lines changed

12 files changed

+564
-237
lines changed

spdlog/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ spdlog-internal = { version = "=0.1.0", path = "../spdlog-internal", optional =
5757
spdlog-macros = { version = "0.1.0", path = "../spdlog-macros" }
5858
spin = "0.9.8"
5959
thiserror = "1.0.37"
60+
toml = "0.8.8"
6061

6162
[target.'cfg(windows)'.dependencies]
6263
winapi = { version = "0.3.9", features = ["consoleapi", "debugapi", "handleapi", "processenv", "processthreadsapi", "winbase", "wincon"] }

spdlog/src/config/deser.rs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
use std::{marker::PhantomData, result::Result as StdResult};
2+
3+
use erased_serde::Deserializer as ErasedDeserializer;
4+
use serde::{
5+
de::{
6+
value::{MapAccessDeserializer, UnitDeserializer},
7+
Error as SerdeDeError, MapAccess, Visitor,
8+
},
9+
Deserialize, Deserializer,
10+
};
11+
12+
use crate::{config, formatter::*, sync::*, Logger, LoggerBuilder, LoggerParams, Result, Sink};
13+
14+
trait Component {
15+
type Value;
16+
17+
fn expecting(formatter: &mut std::fmt::Formatter) -> std::fmt::Result;
18+
fn build(name: &str, de: &mut dyn ErasedDeserializer) -> Result<Self::Value>;
19+
}
20+
21+
struct ComponentFormatter;
22+
23+
impl Component for ComponentFormatter {
24+
type Value = Box<dyn Formatter>;
25+
26+
fn expecting(formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
27+
formatter.write_str("a spdlog-rs formatter")
28+
}
29+
30+
fn build(name: &str, de: &mut dyn ErasedDeserializer) -> Result<Self::Value> {
31+
config::registry().build_formatter(&name, de)
32+
}
33+
}
34+
35+
struct ComponentSink;
36+
37+
impl Component for ComponentSink {
38+
type Value = Arc<dyn Sink>;
39+
40+
fn expecting(formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
41+
formatter.write_str("a spdlog-rs sink")
42+
}
43+
44+
fn build(name: &str, de: &mut dyn ErasedDeserializer) -> Result<Self::Value> {
45+
config::registry().build_sink(&name, de)
46+
}
47+
}
48+
49+
// Unit for 0 parameter components, map for components with parameters
50+
struct UnitOrMapDeserializer<A> {
51+
map: A,
52+
}
53+
54+
impl<'de, A> Deserializer<'de> for UnitOrMapDeserializer<A>
55+
where
56+
A: MapAccess<'de>,
57+
{
58+
type Error = A::Error;
59+
60+
fn deserialize_any<V>(self, visitor: V) -> StdResult<V::Value, Self::Error>
61+
where
62+
V: Visitor<'de>,
63+
{
64+
visitor.visit_map(self.map)
65+
}
66+
67+
fn deserialize_unit<V>(self, visitor: V) -> StdResult<V::Value, Self::Error>
68+
where
69+
V: Visitor<'de>,
70+
{
71+
visitor.visit_unit()
72+
}
73+
74+
fn deserialize_newtype_struct<V>(
75+
self,
76+
name: &'static str,
77+
visitor: V,
78+
) -> StdResult<V::Value, Self::Error>
79+
where
80+
V: Visitor<'de>,
81+
{
82+
visitor.visit_newtype_struct(self)
83+
}
84+
85+
serde::forward_to_deserialize_any! {
86+
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
87+
bytes byte_buf option enum unit_struct seq tuple
88+
tuple_struct map struct identifier ignored_any
89+
}
90+
}
91+
92+
struct ComponentVisitor<C>(PhantomData<C>);
93+
94+
impl<'de, C> Visitor<'de> for ComponentVisitor<C>
95+
where
96+
C: Component,
97+
{
98+
type Value = C::Value;
99+
100+
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
101+
C::expecting(formatter)
102+
}
103+
104+
fn visit_map<A>(self, mut map: A) -> StdResult<Self::Value, A::Error>
105+
where
106+
A: MapAccess<'de>,
107+
{
108+
let name = map
109+
.next_entry::<String, String>()?
110+
.filter(|(key, _)| key == "name")
111+
.map(|(_, value)| value)
112+
.ok_or_else(|| SerdeDeError::missing_field("name"))?;
113+
114+
let mut erased_de = <dyn ErasedDeserializer>::erase(UnitOrMapDeserializer { map });
115+
let component = C::build(&name, &mut erased_de).map_err(SerdeDeError::custom)?;
116+
117+
Ok(component)
118+
}
119+
}
120+
121+
pub fn formatter<'de, D>(de: D) -> StdResult<Option<Box<dyn Formatter>>, D::Error>
122+
where
123+
D: Deserializer<'de>,
124+
{
125+
Ok(Some(de.deserialize_map(ComponentVisitor::<
126+
ComponentFormatter,
127+
>(PhantomData))?))
128+
}
129+
130+
pub fn sink<'de, D>(de: D) -> StdResult<Option<Arc<dyn Sink>>, D::Error>
131+
where
132+
D: Deserializer<'de>,
133+
{
134+
Ok(Some(de.deserialize_map(
135+
ComponentVisitor::<ComponentSink>(PhantomData),
136+
)?))
137+
}
138+
139+
pub fn logger<'de, D>(de: D) -> StdResult<Logger, D::Error>
140+
where
141+
D: Deserializer<'de>,
142+
{
143+
let params = LoggerParams::deserialize(de)?;
144+
LoggerBuilder::build_config(params).map_err(SerdeDeError::custom)
145+
}

spdlog/src/config/mod.rs

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,114 @@
11
mod registry;
22
mod source;
33

4-
pub(crate) mod parse;
4+
pub(crate) mod deser;
5+
6+
use std::{collections::HashMap, convert::Infallible};
57

68
pub use registry::*;
79
use serde::{de::DeserializeOwned, Deserialize};
810
pub use source::*;
911

1012
use crate::{sync::*, Result};
1113

12-
// TODO: Force `'static` on name?
13-
// Builder?
14+
// TODO: Builder?
1415
#[derive(PartialEq, Eq, Hash)]
15-
pub struct ComponentMetadata<'a> {
16-
pub(crate) name: &'a str,
16+
pub struct ComponentMetadata {
17+
name: &'static str,
18+
}
19+
20+
impl ComponentMetadata {
21+
pub fn builder() -> ComponentMetadataBuilder<()> {
22+
ComponentMetadataBuilder { name: () }
23+
}
24+
}
25+
26+
pub struct ComponentMetadataBuilder<ArgName> {
27+
name: ArgName,
28+
}
29+
30+
impl<ArgName> ComponentMetadataBuilder<ArgName> {
31+
pub fn name(self, name: &'static str) -> ComponentMetadataBuilder<&'static str> {
32+
ComponentMetadataBuilder { name }
33+
}
34+
}
35+
36+
impl ComponentMetadataBuilder<()> {
37+
#[doc(hidden)]
38+
#[deprecated(note = "\n\n\
39+
builder compile-time error:\n\
40+
- missing required field `name`\n\n\
41+
")]
42+
pub fn build(self, _: Infallible) {}
43+
}
44+
45+
impl ComponentMetadataBuilder<&'static str> {
46+
pub fn build(self) -> ComponentMetadata {
47+
ComponentMetadata { name: self.name }
48+
}
1749
}
1850

1951
pub trait Configurable: Sized {
2052
type Params: DeserializeOwned + Default + Send;
2153

22-
fn metadata() -> ComponentMetadata<'static>;
54+
fn metadata() -> ComponentMetadata;
2355
fn build(params: Self::Params) -> Result<Self>;
2456
}
57+
58+
mod storage {
59+
use serde::Deserialize;
60+
61+
use super::*;
62+
63+
#[derive(Deserialize)]
64+
#[serde(deny_unknown_fields)]
65+
pub(super) struct Logger(
66+
#[serde(deserialize_with = "crate::config::deser::logger")] crate::Logger,
67+
);
68+
69+
#[derive(Deserialize)]
70+
#[serde(deny_unknown_fields)]
71+
pub(super) struct Config {
72+
loggers: HashMap<String, Logger>,
73+
}
74+
}
75+
76+
pub struct Config(storage::Config);
77+
78+
impl Config {
79+
// TODO: Remember to remove me
80+
pub fn new_for_test(inputs: &str) -> Result<Self> {
81+
let config = toml::from_str(inputs).unwrap();
82+
Ok(Self(config))
83+
}
84+
}
85+
86+
#[cfg(test)]
87+
mod tests {
88+
use super::*;
89+
use crate::test_utils::{config::*, TEST_LOGS_PATH};
90+
91+
#[test]
92+
fn full() {
93+
let inputs = format!(
94+
r#"
95+
[loggers.default]
96+
sinks = [
97+
{{ name = "$ConfigMockSink1", arg = 114 }},
98+
{{ name = "$ConfigMockSink2", arg = 514 }},
99+
{{ name = "$ConfigMockSink3", arg = 1919 }},
100+
{{ name = "FileSink", path = "{}", formatter = {{ name = "PatternFormatter", template = "114514 {{payload}}{{eol}}" }} }}
101+
]
102+
# flush_level_filter = "all" # TODO: design the syntax
103+
# TODO: flush_period = "10s"
104+
"#,
105+
TEST_LOGS_PATH.join("unit_test_config_full.log").display()
106+
);
107+
108+
register_global();
109+
110+
Config::new_for_test(&inputs).unwrap();
111+
112+
// TODO
113+
}
114+
}

spdlog/src/config/parse.rs

Lines changed: 0 additions & 53 deletions
This file was deleted.

0 commit comments

Comments
 (0)