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 pub task: Vec<ConfigItem>,
54 pub mesh: Vec<ConfigItem>,
55}
56
57impl Config {
58 #[must_use]
59 pub fn empty() -> Self {
60 Self {
61 vertex: Default::default(),
62 fragment: Default::default(),
63 compute: Default::default(),
64 task: Default::default(),
65 mesh: Default::default(),
66 }
67 }
68
69 pub fn from_path(path: impl AsRef<Path>) -> anyhow::Result<Config> {
70 let path = path.as_ref();
71 let raw_config = fs::read_to_string(path)
72 .map_err(|source| BadRonParse(BadRonParseKind::Read { source }))?;
73 let config = Config::deserialize_ron(&raw_config)
74 .map_err(|source| BadRonParse(BadRonParseKind::Parse { source }))?;
75 ensure!(!config.is_empty(), BadRonParse(BadRonParseKind::Empty));
76 Ok(config)
77 }
78
79 pub fn to_file(&self, path: impl AsRef<Path>) -> anyhow::Result<()> {
80 let path = path.as_ref();
81 let mut s = self.serialize_ron();
82 s.push('\n');
83 fs::write(path, &s).map_err(|e| anyhow!("failed to write to {}: {e}", path.display()))
84 }
85
86 #[must_use]
87 pub fn is_empty(&self) -> bool {
88 let Self {
89 vertex,
90 fragment,
91 compute,
92 task,
93 mesh,
94 } = self;
95 vertex.is_empty()
96 && fragment.is_empty()
97 && compute.is_empty()
98 && task.is_empty()
99 && mesh.is_empty()
100 }
101}
102
103#[derive(Debug, DeRon, SerRon)]
104pub struct ConfigItem {
105 pub entry_point: String,
106 pub target_profile: String,
109}