oxygengine_build_tools/
lib.rs1use serde::Deserialize;
2use std::{collections::HashMap, fs::read_to_string, io::Write, path::PathBuf};
3
4pub trait ParamsFromArgs: Sized {
5 fn params_from_args(_args: impl Iterator<Item = String>) -> Option<Self> {
6 None
7 }
8}
9
10#[derive(Debug, Default, Clone)]
11pub struct StructuredArguments(HashMap<String, Vec<String>>);
12
13impl StructuredArguments {
14 pub fn read(&self, name: &'static str) -> Option<impl Iterator<Item = &str>> {
15 Some(self.0.get(name)?.iter().map(|item| item.as_str()))
16 }
17
18 pub fn read_many(&self, name: &'static [&'static str]) -> impl Iterator<Item = &str> {
19 name.iter().filter_map(|name| self.read(name)).flatten()
20 }
21
22 pub fn read_default(&self) -> impl Iterator<Item = &str> {
23 self.read("").unwrap()
24 }
25
26 pub fn consume(&mut self, name: &'static str) -> Option<impl Iterator<Item = String>> {
27 Some(self.0.remove(name)?.into_iter())
28 }
29
30 pub fn consume_many<'a>(
31 &'a mut self,
32 name: &'static [&'static str],
33 ) -> impl Iterator<Item = String> + 'a {
34 name.iter().filter_map(|name| self.consume(name)).flatten()
35 }
36
37 pub fn consume_default(&mut self) -> impl Iterator<Item = String> {
38 self.consume("").unwrap()
39 }
40
41 pub fn new(args: impl Iterator<Item = String>) -> StructuredArguments {
42 let mut result = HashMap::<_, Vec<_>>::default();
43 result.insert(Default::default(), Default::default());
44 let mut name = String::new();
45 for arg in args {
46 if let Some(arg) = arg.strip_prefix("--") {
47 name = arg.to_owned();
48 } else if arg.starts_with('-') && arg.len() == 2 {
49 name = arg[1..].to_owned()
50 } else {
51 result.entry(name.to_owned()).or_default().push(arg);
52 }
53 }
54 StructuredArguments(result)
55 }
56}
57
58pub struct AssetPipelinePlugin;
59
60impl AssetPipelinePlugin {
61 pub fn run<T, E>(
62 f: impl FnOnce(AssetPipelineInput<T>) -> Result<Vec<String>, E>,
63 ) -> Result<(), E>
64 where
65 T: for<'de> Deserialize<'de> + ParamsFromArgs,
66 {
67 let output = f(AssetPipelineInput::<T>::consume())?;
68 serde_json::to_writer(std::io::stdout(), &output)
69 .expect("Could not serialize output content");
70 let _ = std::io::stdout().flush();
71 Ok(())
72 }
73}
74
75pub struct AssetPipelineInput<T> {
76 pub source: Vec<PathBuf>,
77 pub target: PathBuf,
78 pub assets: String,
79 pub params: T,
80}
81
82impl<T> AssetPipelineInput<T> {
83 fn consume() -> Self
84 where
85 T: for<'de> Deserialize<'de> + ParamsFromArgs,
86 {
87 let mut args = std::env::args();
88 args.next();
89 let mut source = vec![];
90 let mut target = Default::default();
91 let mut assets = Default::default();
92 for arg in args.by_ref() {
93 if arg == "--" {
94 break;
95 } else {
96 source.push(arg.into());
97 }
98 }
99 for arg in args.by_ref() {
100 if arg == "--" {
101 break;
102 } else {
103 target = arg.into();
104 }
105 }
106 for arg in args.by_ref() {
107 if arg == "--" {
108 break;
109 } else {
110 assets = arg;
111 }
112 }
113 let params = if let Some(arg) = args.next() {
114 if arg == "--" {
115 T::params_from_args(args).expect("Could not read args input content")
116 } else if let Some((t, i)) = arg.find('=').map(|i| (&arg[0..i], i + 1)) {
117 let content = match t {
118 "file" => {
119 let path = &arg[i..];
120 read_to_string(path)
121 .unwrap_or_else(|_| panic!("Could not read file: {}", path))
122 }
123 "data" => arg[i..].to_owned(),
124 name => panic!("Unexpected type: {}", name),
125 };
126 serde_json::from_str::<T>(&content).expect("Could not deserialize input content")
127 } else {
128 panic!("Wrong input: {:?}", arg)
129 }
130 } else {
131 serde_json::from_reader::<_, T>(std::io::stdin())
132 .expect("Could not deserialize input stream")
133 };
134 Self {
135 source,
136 target,
137 assets,
138 params,
139 }
140 }
141}