1use std::{error::Error, fmt::Display, fs, io, path::Path};
2
3use anyhow::{anyhow, ensure};
4use nanoserde::{self, DeRon, DeRonErr, SerRon};
5
6#[derive(Debug)]
7struct BadRonParse(BadRonParseKind);
8
9impl Display for BadRonParse {
10 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11 write!(f, "failed to read RON configuration of HLSL snapshot test")
12 }
13}
14
15impl Error for BadRonParse {
16 fn source(&self) -> Option<&(dyn Error + 'static)> {
17 Some(&self.0)
18 }
19}
20
21#[derive(Debug)]
22enum BadRonParseKind {
23 Read { source: io::Error },
24 Parse { source: DeRonErr },
25 Empty,
26}
27
28impl Display for BadRonParseKind {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 match self {
31 BadRonParseKind::Read { source } => Display::fmt(source, f),
32 BadRonParseKind::Parse { source } => Display::fmt(source, f),
33 BadRonParseKind::Empty => write!(f, "no configuration was specified"),
34 }
35 }
36}
37
38impl Error for BadRonParseKind {
39 fn source(&self) -> Option<&(dyn Error + 'static)> {
40 match self {
41 BadRonParseKind::Read { source } => source.source(),
42 BadRonParseKind::Parse { source } => source.source(),
43 BadRonParseKind::Empty => None,
44 }
45 }
46}
47
48#[derive(Debug, DeRon, SerRon)]
49pub struct Config {
50 pub vertex: Vec<ConfigItem>,
51 pub fragment: Vec<ConfigItem>,
52 pub compute: Vec<ConfigItem>,
53}
54
55impl Config {
56 #[must_use]
57 pub fn empty() -> Self {
58 Self {
59 vertex: Default::default(),
60 fragment: Default::default(),
61 compute: Default::default(),
62 }
63 }
64
65 pub fn from_path(path: impl AsRef<Path>) -> anyhow::Result<Config> {
66 let path = path.as_ref();
67 let raw_config = fs::read_to_string(path)
68 .map_err(|source| BadRonParse(BadRonParseKind::Read { source }))?;
69 let config = Config::deserialize_ron(&raw_config)
70 .map_err(|source| BadRonParse(BadRonParseKind::Parse { source }))?;
71 ensure!(!config.is_empty(), BadRonParse(BadRonParseKind::Empty));
72 Ok(config)
73 }
74
75 pub fn to_file(&self, path: impl AsRef<Path>) -> anyhow::Result<()> {
76 let path = path.as_ref();
77 let mut s = self.serialize_ron();
78 s.push('\n');
79 fs::write(path, &s).map_err(|e| anyhow!("failed to write to {}: {e}", path.display()))
80 }
81
82 #[must_use]
83 pub fn is_empty(&self) -> bool {
84 let Self {
85 vertex,
86 fragment,
87 compute,
88 } = self;
89 vertex.is_empty() && fragment.is_empty() && compute.is_empty()
90 }
91}
92
93#[derive(Debug, DeRon, SerRon)]
94pub struct ConfigItem {
95 pub entry_point: String,
96 pub target_profile: String,
99}