Skip to main content

subsoil/tracing/
types.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later WITH Classpath-exception-2.0
6
7use alloc::{vec, vec::Vec};
8use codec::{Decode, Encode};
9/// Types for wasm based tracing. Loosely inspired by `tracing-core` but
10/// optimised for the specific use case.
11use core::{fmt::Debug, format_args};
12
13/// The Tracing Level – the user can filter by this
14#[derive(Clone, Encode, Decode, Debug)]
15pub enum WasmLevel {
16	/// This is a fatal errors
17	ERROR,
18	/// This is a warning you should be aware of
19	WARN,
20	/// Nice to now info
21	INFO,
22	/// Further information for debugging purposes
23	DEBUG,
24	/// The lowest level, keeping track of minute detail
25	TRACE,
26}
27
28impl From<&tracing_core::Level> for WasmLevel {
29	fn from(l: &tracing_core::Level) -> WasmLevel {
30		match *l {
31			tracing_core::Level::ERROR => WasmLevel::ERROR,
32			tracing_core::Level::WARN => WasmLevel::WARN,
33			tracing_core::Level::INFO => WasmLevel::INFO,
34			tracing_core::Level::DEBUG => WasmLevel::DEBUG,
35			tracing_core::Level::TRACE => WasmLevel::TRACE,
36		}
37	}
38}
39
40impl core::default::Default for WasmLevel {
41	fn default() -> Self {
42		WasmLevel::TRACE
43	}
44}
45
46/// A parameter value provided to the span/event
47#[derive(Encode, Decode, Clone)]
48pub enum WasmValue {
49	U8(u8),
50	I8(i8),
51	U32(u32),
52	I32(i32),
53	I64(i64),
54	U64(u64),
55	Bool(bool),
56	Str(Vec<u8>),
57	/// Debug or Display call, this is most-likely a print-able UTF8 String
58	Formatted(Vec<u8>),
59	/// SCALE CODEC encoded object – the name should allow the received to know
60	/// how to decode this.
61	Encoded(Vec<u8>),
62}
63
64impl core::fmt::Debug for WasmValue {
65	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
66		match self {
67			WasmValue::U8(ref i) => f.write_fmt(format_args!("{}_u8", i)),
68			WasmValue::I8(ref i) => f.write_fmt(format_args!("{}_i8", i)),
69			WasmValue::U32(ref i) => f.write_fmt(format_args!("{}_u32", i)),
70			WasmValue::I32(ref i) => f.write_fmt(format_args!("{}_i32", i)),
71			WasmValue::I64(ref i) => f.write_fmt(format_args!("{}_i64", i)),
72			WasmValue::U64(ref i) => f.write_fmt(format_args!("{}_u64", i)),
73			WasmValue::Bool(ref i) => f.write_fmt(format_args!("{}_bool", i)),
74			WasmValue::Formatted(ref i) | WasmValue::Str(ref i) => {
75				if let Ok(v) = core::str::from_utf8(i) {
76					f.write_fmt(format_args!("{}", v))
77				} else {
78					f.write_fmt(format_args!("{:?}", i))
79				}
80			},
81			WasmValue::Encoded(ref v) => {
82				f.write_str("Scale(")?;
83				for byte in v {
84					f.write_fmt(format_args!("{:02x}", byte))?;
85				}
86				f.write_str(")")
87			},
88		}
89	}
90}
91
92impl From<u8> for WasmValue {
93	fn from(u: u8) -> WasmValue {
94		WasmValue::U8(u)
95	}
96}
97
98impl From<&i8> for WasmValue {
99	fn from(inp: &i8) -> WasmValue {
100		WasmValue::I8(*inp)
101	}
102}
103
104impl From<&str> for WasmValue {
105	fn from(inp: &str) -> WasmValue {
106		WasmValue::Str(inp.as_bytes().to_vec())
107	}
108}
109
110impl From<&&str> for WasmValue {
111	fn from(inp: &&str) -> WasmValue {
112		WasmValue::Str((*inp).as_bytes().to_vec())
113	}
114}
115
116impl From<bool> for WasmValue {
117	fn from(inp: bool) -> WasmValue {
118		WasmValue::Bool(inp)
119	}
120}
121
122impl From<core::fmt::Arguments<'_>> for WasmValue {
123	fn from(inp: core::fmt::Arguments<'_>) -> WasmValue {
124		let mut buf = alloc::string::String::default();
125		core::fmt::write(&mut buf, inp).expect("Writing of arguments doesn't fail");
126		WasmValue::Formatted(buf.into_bytes())
127	}
128}
129
130impl From<i8> for WasmValue {
131	fn from(u: i8) -> WasmValue {
132		WasmValue::I8(u)
133	}
134}
135
136impl From<i32> for WasmValue {
137	fn from(u: i32) -> WasmValue {
138		WasmValue::I32(u)
139	}
140}
141
142impl From<&i32> for WasmValue {
143	fn from(u: &i32) -> WasmValue {
144		WasmValue::I32(*u)
145	}
146}
147
148impl From<u32> for WasmValue {
149	fn from(u: u32) -> WasmValue {
150		WasmValue::U32(u)
151	}
152}
153
154impl From<&u32> for WasmValue {
155	fn from(u: &u32) -> WasmValue {
156		WasmValue::U32(*u)
157	}
158}
159
160impl From<u64> for WasmValue {
161	fn from(u: u64) -> WasmValue {
162		WasmValue::U64(u)
163	}
164}
165
166impl From<i64> for WasmValue {
167	fn from(u: i64) -> WasmValue {
168		WasmValue::I64(u)
169	}
170}
171
172/// The name of a field provided as the argument name when constructing an
173/// `event!` or `span!`.
174/// Generally generated automatically via `stringify` from an `'static &str`.
175/// Likely print-able.
176#[derive(Encode, Decode, Clone)]
177pub struct WasmFieldName(Vec<u8>);
178
179impl core::fmt::Debug for WasmFieldName {
180	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
181		if let Ok(v) = core::str::from_utf8(&self.0) {
182			f.write_fmt(format_args!("{}", v))
183		} else {
184			for byte in self.0.iter() {
185				f.write_fmt(format_args!("{:02x}", byte))?;
186			}
187			Ok(())
188		}
189	}
190}
191
192impl From<Vec<u8>> for WasmFieldName {
193	fn from(v: Vec<u8>) -> Self {
194		WasmFieldName(v)
195	}
196}
197
198impl From<&str> for WasmFieldName {
199	fn from(v: &str) -> Self {
200		WasmFieldName(v.as_bytes().to_vec())
201	}
202}
203
204/// A list of `WasmFieldName`s in the order provided
205#[derive(Encode, Decode, Clone, Debug)]
206pub struct WasmFields(Vec<WasmFieldName>);
207
208impl WasmFields {
209	/// Iterate over the fields
210	pub fn iter(&self) -> core::slice::Iter<'_, WasmFieldName> {
211		self.0.iter()
212	}
213}
214
215impl From<Vec<WasmFieldName>> for WasmFields {
216	fn from(v: Vec<WasmFieldName>) -> WasmFields {
217		WasmFields(v)
218	}
219}
220
221impl From<Vec<&str>> for WasmFields {
222	fn from(v: Vec<&str>) -> WasmFields {
223		WasmFields(v.into_iter().map(|v| v.into()).collect())
224	}
225}
226
227impl WasmFields {
228	/// Create an empty entry
229	pub fn empty() -> Self {
230		WasmFields(Vec::with_capacity(0))
231	}
232}
233
234impl From<&tracing_core::field::FieldSet> for WasmFields {
235	fn from(wm: &tracing_core::field::FieldSet) -> WasmFields {
236		WasmFields(wm.iter().map(|s| s.name().into()).collect())
237	}
238}
239
240/// A list of `WasmFieldName`s with the given `WasmValue` (if provided)
241/// in the order specified.
242#[derive(Encode, Decode, Clone)]
243pub struct WasmValuesSet(Vec<(WasmFieldName, Option<WasmValue>)>);
244
245impl core::fmt::Debug for WasmValuesSet {
246	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
247		let mut wrt = f.debug_struct("");
248		let mut non_str = false;
249		for (f, v) in self.0.iter() {
250			if let Ok(s) = core::str::from_utf8(&f.0) {
251				match v {
252					Some(ref i) => wrt.field(s, i),
253					None => wrt.field(s, &(None as Option<WasmValue>)),
254				};
255			} else {
256				non_str = true;
257			}
258		}
259
260		// FIXME: replace with using `finish_non_exhaustive()` once stable
261		//        https://github.com/rust-lang/rust/issues/67364
262		if non_str {
263			wrt.field("..", &"..");
264		}
265
266		wrt.finish()
267	}
268}
269
270impl From<Vec<(WasmFieldName, Option<WasmValue>)>> for WasmValuesSet {
271	fn from(v: Vec<(WasmFieldName, Option<WasmValue>)>) -> Self {
272		WasmValuesSet(v)
273	}
274}
275impl From<Vec<(&&WasmFieldName, Option<WasmValue>)>> for WasmValuesSet {
276	fn from(v: Vec<(&&WasmFieldName, Option<WasmValue>)>) -> Self {
277		WasmValuesSet(v.into_iter().map(|(k, v)| ((**k).clone(), v)).collect())
278	}
279}
280
281impl From<Vec<(&&str, Option<WasmValue>)>> for WasmValuesSet {
282	fn from(v: Vec<(&&str, Option<WasmValue>)>) -> Self {
283		WasmValuesSet(v.into_iter().map(|(k, v)| ((*k).into(), v)).collect())
284	}
285}
286
287impl WasmValuesSet {
288	/// Create an empty entry
289	pub fn empty() -> Self {
290		WasmValuesSet(Vec::with_capacity(0))
291	}
292}
293
294impl tracing_core::field::Visit for WasmValuesSet {
295	fn record_debug(&mut self, field: &tracing_core::field::Field, value: &dyn Debug) {
296		self.0
297			.push((field.name().into(), Some(WasmValue::from(format_args!("{:?}", value)))))
298	}
299	fn record_i64(&mut self, field: &tracing_core::field::Field, value: i64) {
300		self.0.push((field.name().into(), Some(WasmValue::from(value))))
301	}
302	fn record_u64(&mut self, field: &tracing_core::field::Field, value: u64) {
303		self.0.push((field.name().into(), Some(WasmValue::from(value))))
304	}
305	fn record_bool(&mut self, field: &tracing_core::field::Field, value: bool) {
306		self.0.push((field.name().into(), Some(WasmValue::from(value))))
307	}
308	fn record_str(&mut self, field: &tracing_core::field::Field, value: &str) {
309		self.0.push((field.name().into(), Some(WasmValue::from(value))))
310	}
311}
312/// Metadata provides generic information about the specific location of the
313/// `span!` or `event!` call on the wasm-side.
314#[derive(Encode, Decode, Clone)]
315pub struct WasmMetadata {
316	/// The name given to `event!`/`span!`, `&'static str` converted to bytes
317	pub name: Vec<u8>,
318	/// The given target to `event!`/`span!` – or module-name, `&'static str` converted to bytes
319	pub target: Vec<u8>,
320	/// The level of this entry
321	pub level: WasmLevel,
322	/// The file this was emitted from – useful for debugging;  `&'static str` converted to bytes
323	pub file: Vec<u8>,
324	/// The specific line number in the file – useful for debugging
325	pub line: u32,
326	/// The module path;  `&'static str` converted to bytes
327	pub module_path: Vec<u8>,
328	/// Whether this is a call  to `span!` or `event!`
329	pub is_span: bool,
330	/// The list of fields specified in the call
331	pub fields: WasmFields,
332}
333
334impl From<&tracing_core::Metadata<'_>> for WasmMetadata {
335	fn from(wm: &tracing_core::Metadata<'_>) -> WasmMetadata {
336		WasmMetadata {
337			name: wm.name().as_bytes().to_vec(),
338			target: wm.target().as_bytes().to_vec(),
339			level: wm.level().into(),
340			file: wm.file().map(|f| f.as_bytes().to_vec()).unwrap_or_default(),
341			line: wm.line().unwrap_or_default(),
342			module_path: wm.module_path().map(|m| m.as_bytes().to_vec()).unwrap_or_default(),
343			is_span: wm.is_span(),
344			fields: wm.fields().into(),
345		}
346	}
347}
348
349impl core::fmt::Debug for WasmMetadata {
350	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
351		f.debug_struct("WasmMetadata")
352			.field("name", &decode_field(&self.name))
353			.field("target", &decode_field(&self.target))
354			.field("level", &self.level)
355			.field("file", &decode_field(&self.file))
356			.field("line", &self.line)
357			.field("module_path", &decode_field(&self.module_path))
358			.field("is_span", &self.is_span)
359			.field("fields", &self.fields)
360			.finish()
361	}
362}
363
364impl core::default::Default for WasmMetadata {
365	fn default() -> Self {
366		let target = "default".as_bytes().to_vec();
367		WasmMetadata {
368			target,
369			name: Default::default(),
370			level: Default::default(),
371			file: Default::default(),
372			line: Default::default(),
373			module_path: Default::default(),
374			is_span: true,
375			fields: WasmFields::empty(),
376		}
377	}
378}
379
380fn decode_field(field: &[u8]) -> &str {
381	core::str::from_utf8(field).unwrap_or_default()
382}
383
384/// Span or Event Attributes
385#[derive(Encode, Decode, Clone, Debug)]
386pub struct WasmEntryAttributes {
387	/// the parent, if directly specified – otherwise assume most inner span
388	pub parent_id: Option<u64>,
389	/// the metadata of the location
390	pub metadata: WasmMetadata,
391	/// the Values provided
392	pub fields: WasmValuesSet,
393}
394
395impl From<&tracing_core::Event<'_>> for WasmEntryAttributes {
396	fn from(evt: &tracing_core::Event<'_>) -> WasmEntryAttributes {
397		let mut fields = WasmValuesSet(Vec::new());
398		evt.record(&mut fields);
399		WasmEntryAttributes {
400			parent_id: evt.parent().map(|id| id.into_u64()),
401			metadata: evt.metadata().into(),
402			fields,
403		}
404	}
405}
406
407impl From<&tracing_core::span::Attributes<'_>> for WasmEntryAttributes {
408	fn from(attrs: &tracing_core::span::Attributes<'_>) -> WasmEntryAttributes {
409		let mut fields = WasmValuesSet(Vec::new());
410		attrs.record(&mut fields);
411		WasmEntryAttributes {
412			parent_id: attrs.parent().map(|id| id.into_u64()),
413			metadata: attrs.metadata().into(),
414			fields,
415		}
416	}
417}
418
419impl core::default::Default for WasmEntryAttributes {
420	fn default() -> Self {
421		WasmEntryAttributes {
422			parent_id: None,
423			metadata: Default::default(),
424			fields: WasmValuesSet(vec![]),
425		}
426	}
427}
428
429#[cfg(not(substrate_runtime))]
430mod std_features {
431
432	use tracing_core::callsite;
433
434	/// Static entry use for wasm-originated metadata.
435	pub struct WasmCallsite;
436	impl callsite::Callsite for WasmCallsite {
437		fn set_interest(&self, _: tracing_core::Interest) {
438			unimplemented!()
439		}
440		fn metadata(&self) -> &tracing_core::Metadata<'_> {
441			unimplemented!()
442		}
443	}
444	static CALLSITE: WasmCallsite = WasmCallsite;
445	/// The identifier we are using to inject the wasm events in the generic `tracing` system
446	pub static WASM_TRACE_IDENTIFIER: &str = "wasm_tracing";
447	/// The fieldname for the wasm-originated name
448	pub static WASM_NAME_KEY: &str = "name";
449	/// The fieldname for the wasm-originated target
450	pub static WASM_TARGET_KEY: &str = "target";
451	/// The the list of all static field names we construct from the given metadata
452	pub static GENERIC_FIELDS: &[&str] =
453		&[WASM_TARGET_KEY, WASM_NAME_KEY, "file", "line", "module_path", "params"];
454
455	// Implementation Note:
456	// the original `tracing` crate generates these static metadata entries at every `span!` and
457	// `event!` location to allow for highly optimised filtering. For us to allow level-based
458	// emitting of wasm events we need these static metadata entries to inject into that system. We
459	// then provide generic `From`-implementations picking the right metadata to refer to.
460
461	static SPAN_ERROR_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
462		WASM_TRACE_IDENTIFIER,
463		WASM_TRACE_IDENTIFIER,
464		tracing::Level::ERROR,
465		None,
466		None,
467		None,
468		tracing_core::field::FieldSet::new(
469			GENERIC_FIELDS,
470			tracing_core::identify_callsite!(&CALLSITE),
471		),
472		tracing_core::metadata::Kind::SPAN,
473	);
474
475	static SPAN_WARN_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
476		WASM_TRACE_IDENTIFIER,
477		WASM_TRACE_IDENTIFIER,
478		tracing::Level::WARN,
479		None,
480		None,
481		None,
482		tracing_core::field::FieldSet::new(
483			GENERIC_FIELDS,
484			tracing_core::identify_callsite!(&CALLSITE),
485		),
486		tracing_core::metadata::Kind::SPAN,
487	);
488	static SPAN_INFO_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
489		WASM_TRACE_IDENTIFIER,
490		WASM_TRACE_IDENTIFIER,
491		tracing::Level::INFO,
492		None,
493		None,
494		None,
495		tracing_core::field::FieldSet::new(
496			GENERIC_FIELDS,
497			tracing_core::identify_callsite!(&CALLSITE),
498		),
499		tracing_core::metadata::Kind::SPAN,
500	);
501
502	static SPAN_DEBUG_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
503		WASM_TRACE_IDENTIFIER,
504		WASM_TRACE_IDENTIFIER,
505		tracing::Level::DEBUG,
506		None,
507		None,
508		None,
509		tracing_core::field::FieldSet::new(
510			GENERIC_FIELDS,
511			tracing_core::identify_callsite!(&CALLSITE),
512		),
513		tracing_core::metadata::Kind::SPAN,
514	);
515
516	static SPAN_TRACE_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
517		WASM_TRACE_IDENTIFIER,
518		WASM_TRACE_IDENTIFIER,
519		tracing::Level::TRACE,
520		None,
521		None,
522		None,
523		tracing_core::field::FieldSet::new(
524			GENERIC_FIELDS,
525			tracing_core::identify_callsite!(&CALLSITE),
526		),
527		tracing_core::metadata::Kind::SPAN,
528	);
529
530	static EVENT_ERROR_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
531		WASM_TRACE_IDENTIFIER,
532		WASM_TRACE_IDENTIFIER,
533		tracing::Level::ERROR,
534		None,
535		None,
536		None,
537		tracing_core::field::FieldSet::new(
538			GENERIC_FIELDS,
539			tracing_core::identify_callsite!(&CALLSITE),
540		),
541		tracing_core::metadata::Kind::EVENT,
542	);
543
544	static EVENT_WARN_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
545		WASM_TRACE_IDENTIFIER,
546		WASM_TRACE_IDENTIFIER,
547		tracing::Level::WARN,
548		None,
549		None,
550		None,
551		tracing_core::field::FieldSet::new(
552			GENERIC_FIELDS,
553			tracing_core::identify_callsite!(&CALLSITE),
554		),
555		tracing_core::metadata::Kind::EVENT,
556	);
557
558	static EVENT_INFO_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
559		WASM_TRACE_IDENTIFIER,
560		WASM_TRACE_IDENTIFIER,
561		tracing::Level::INFO,
562		None,
563		None,
564		None,
565		tracing_core::field::FieldSet::new(
566			GENERIC_FIELDS,
567			tracing_core::identify_callsite!(&CALLSITE),
568		),
569		tracing_core::metadata::Kind::EVENT,
570	);
571
572	static EVENT_DEBUG_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
573		WASM_TRACE_IDENTIFIER,
574		WASM_TRACE_IDENTIFIER,
575		tracing::Level::DEBUG,
576		None,
577		None,
578		None,
579		tracing_core::field::FieldSet::new(
580			GENERIC_FIELDS,
581			tracing_core::identify_callsite!(&CALLSITE),
582		),
583		tracing_core::metadata::Kind::EVENT,
584	);
585
586	static EVENT_TRACE_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
587		WASM_TRACE_IDENTIFIER,
588		WASM_TRACE_IDENTIFIER,
589		tracing::Level::TRACE,
590		None,
591		None,
592		None,
593		tracing_core::field::FieldSet::new(
594			GENERIC_FIELDS,
595			tracing_core::identify_callsite!(&CALLSITE),
596		),
597		tracing_core::metadata::Kind::EVENT,
598	);
599
600	// FIXME: this could be done a lot in 0.2 if they opt for using `Cow<str,'static>` instead
601	// 			https://github.com/paritytech/substrate/issues/7134
602	impl From<&super::WasmMetadata> for &'static tracing_core::Metadata<'static> {
603		fn from(wm: &super::WasmMetadata) -> &'static tracing_core::Metadata<'static> {
604			match (&wm.level, wm.is_span) {
605				(&super::WasmLevel::ERROR, true) => &SPAN_ERROR_METADATA,
606				(&super::WasmLevel::WARN, true) => &SPAN_WARN_METADATA,
607				(&super::WasmLevel::INFO, true) => &SPAN_INFO_METADATA,
608				(&super::WasmLevel::DEBUG, true) => &SPAN_DEBUG_METADATA,
609				(&super::WasmLevel::TRACE, true) => &SPAN_TRACE_METADATA,
610				(&super::WasmLevel::ERROR, false) => &EVENT_ERROR_METADATA,
611				(&super::WasmLevel::WARN, false) => &EVENT_WARN_METADATA,
612				(&super::WasmLevel::INFO, false) => &EVENT_INFO_METADATA,
613				(&super::WasmLevel::DEBUG, false) => &EVENT_DEBUG_METADATA,
614				(&super::WasmLevel::TRACE, false) => &EVENT_TRACE_METADATA,
615			}
616		}
617	}
618
619	impl From<super::WasmEntryAttributes> for tracing::Span {
620		fn from(a: super::WasmEntryAttributes) -> tracing::Span {
621			let name = core::str::from_utf8(&a.metadata.name).unwrap_or_default();
622			let target = core::str::from_utf8(&a.metadata.target).unwrap_or_default();
623			let file = core::str::from_utf8(&a.metadata.file).unwrap_or_default();
624			let line = a.metadata.line;
625			let module_path = core::str::from_utf8(&a.metadata.module_path).unwrap_or_default();
626			let params = a.fields;
627			let metadata: &tracing_core::metadata::Metadata<'static> = (&a.metadata).into();
628
629			tracing::span::Span::child_of(
630				a.parent_id.map(tracing_core::span::Id::from_u64),
631				metadata,
632				&tracing::valueset! { metadata.fields(), target, name, file, line, module_path, ?params },
633			)
634		}
635	}
636
637	impl super::WasmEntryAttributes {
638		/// convert the given Attributes to an event and emit it using `tracing_core`.
639		pub fn emit(self: super::WasmEntryAttributes) {
640			let name = core::str::from_utf8(&self.metadata.name).unwrap_or_default();
641			let target = core::str::from_utf8(&self.metadata.target).unwrap_or_default();
642			let file = core::str::from_utf8(&self.metadata.file).unwrap_or_default();
643			let line = self.metadata.line;
644			let module_path = core::str::from_utf8(&self.metadata.module_path).unwrap_or_default();
645			let params = self.fields;
646			let metadata: &tracing_core::metadata::Metadata<'static> = (&self.metadata).into();
647
648			tracing_core::Event::child_of(
649				self.parent_id.map(tracing_core::span::Id::from_u64),
650				metadata,
651				&tracing::valueset! { metadata.fields(), target, name, file, line, module_path, ?params },
652			)
653		}
654	}
655}
656
657#[cfg(not(substrate_runtime))]
658pub use std_features::*;