1313//! - [`SerializedParam`] resolves to an arbitrary `T`, deserializing the file
1414//! contents using a [`ParamFormat`] marker (for example [`Json`] or
1515//! [`Toml`]). This is useful when the file contents are themselves a
16- //! structured document whose shape and size are only known at runtime.
16+ //! structured document whose shape and size are only known at runtime. The
17+ //! [`Raw`] marker covers the simpler case of a file holding a single bare
18+ //! value such as a uuid.
1719//!
1820//! # TOML Usage
1921//!
@@ -239,6 +241,8 @@ pub trait ParamFormat {
239241#[ derive( Debug , Error ) ]
240242#[ non_exhaustive]
241243pub enum FormatError {
244+ #[ error( "Failed to deserialize as a raw value" ) ]
245+ Raw ( #[ from] serde:: de:: value:: Error ) ,
242246 #[ cfg( feature = "json" ) ]
243247 #[ error( "Failed to deserialize as JSON" ) ]
244248 Json ( #[ from] serde_json:: Error ) ,
@@ -247,6 +251,20 @@ pub enum FormatError {
247251 Toml ( #[ from] toml:: de:: Error ) ,
248252}
249253
254+ #[ derive( Debug , Clone , Copy ) ]
255+ pub struct Raw ;
256+
257+ impl ParamFormat for Raw {
258+ fn deserialize_str < T : DeserializeOwned > ( content : & str ) -> Result < T , FormatError > {
259+ let deserializer =
260+ serde:: de:: value:: StrDeserializer :: < serde:: de:: value:: Error > :: new ( content. trim ( ) ) ;
261+ Ok ( T :: deserialize ( deserializer) ?)
262+ }
263+ }
264+
265+ /// A [`SerializedParam`] that reads its file contents as a single raw value.
266+ pub type RawParam < T > = SerializedParam < T , Raw > ;
267+
250268/// Marker type selecting JSON deserialization for a [`SerializedParam`].
251269#[ cfg( feature = "json" ) ]
252270#[ derive( Debug , Clone , Copy ) ]
@@ -297,7 +315,7 @@ fn read_to_string(path: &Path) -> Result<String, ParamResolutionError> {
297315mod tests {
298316 use super :: * ;
299317 use secrecy:: ExposeSecret ;
300- use std:: io:: Write ;
318+ use std:: { io:: Write , str :: FromStr } ;
301319 use tempfile:: NamedTempFile ;
302320
303321 #[ test]
@@ -389,6 +407,61 @@ mod tests {
389407 ) ;
390408 }
391409
410+ #[ test]
411+ fn test_serialized_raw_from_path ( ) {
412+ // A file holding nothing but the value, with trailing whitespace as a
413+ // file written by `echo` would have.
414+ let mut file = NamedTempFile :: new ( ) . unwrap ( ) ;
415+ writeln ! ( file, " raw-value" ) . unwrap ( ) ;
416+
417+ let param: RawParam < String > = SerializedParam :: from_path ( file. path ( ) ) ;
418+ assert_eq ! ( param. resolve( None ) . unwrap( ) , "raw-value" ) ;
419+ }
420+
421+ #[ test]
422+ fn test_serialized_raw_inline ( ) {
423+ use newtype_uuid:: { TypedUuid , TypedUuidKind , TypedUuidTag } ;
424+
425+ enum TestKind { }
426+ impl TypedUuidKind for TestKind {
427+ fn tag ( ) -> TypedUuidTag {
428+ const TAG : TypedUuidTag = TypedUuidTag :: new ( "test" ) ;
429+ TAG
430+ }
431+ }
432+
433+ #[ derive( Deserialize ) ]
434+ struct Config {
435+ key : RawParam < TypedUuid < TestKind > > ,
436+ }
437+
438+ let raw = "1a1e02f3-5d09-4b7f-b8f1-7b8a2c4d5e6f" ;
439+ let parsed = TypedUuid :: from_str ( raw) . unwrap ( ) ;
440+
441+ let config: Config = toml:: from_str ( & format ! ( "key = \" {}\" " , raw) ) . unwrap ( ) ;
442+ assert_eq ! ( config. key. resolve( None ) . unwrap( ) , parsed) ;
443+ }
444+
445+ #[ test]
446+ fn test_serialized_raw_typed_uuid ( ) {
447+ use newtype_uuid:: { TypedUuid , TypedUuidKind , TypedUuidTag } ;
448+
449+ enum TestKind { }
450+ impl TypedUuidKind for TestKind {
451+ fn tag ( ) -> TypedUuidTag {
452+ const TAG : TypedUuidTag = TypedUuidTag :: new ( "test" ) ;
453+ TAG
454+ }
455+ }
456+
457+ let raw = "1a1e02f3-5d09-4b7f-b8f1-7b8a2c4d5e6f" ;
458+ let mut file = NamedTempFile :: new ( ) . unwrap ( ) ;
459+ writeln ! ( file, "{}" , raw) . unwrap ( ) ;
460+
461+ let param: RawParam < TypedUuid < TestKind > > = SerializedParam :: from_path ( file. path ( ) ) ;
462+ assert_eq ! ( param. resolve( None ) . unwrap( ) . to_string( ) , raw) ;
463+ }
464+
392465 #[ cfg( feature = "json" ) ]
393466 #[ test]
394467 fn test_serialized_json_from_path ( ) {
0 commit comments