1use crate::objects;
2use crate::objects::ObjectKind;
3use serde::{Deserialize, Serialize};
4use std::path::{Path, PathBuf};
5use std::{fs, io};
6
7#[derive(Debug, thiserror::Error)]
9#[non_exhaustive]
10pub enum WriterError {
11    #[error("IO error: {0}")]
12    IO(io::Error),
13
14    #[error("Failed to serialize query: {0}")]
15    Parse(serde_json::Error),
16
17    #[error("Client name not set")]
18    ClientNameNotSet,
19}
20
21impl From<io::Error> for WriterError {
22    fn from(err: io::Error) -> Self {
23        WriterError::IO(err)
24    }
25}
26
27impl From<serde_json::Error> for WriterError {
28    fn from(err: serde_json::Error) -> Self {
29        WriterError::Parse(err)
30    }
31}
32
33#[derive(Default)]
47pub struct Writer {
48    query: Query,
49    client_name: Option<String>,
50}
51
52impl Writer {
53    pub fn request_object<T: objects::Object>(&mut self) -> &mut Self {
55        self.query.requests.push(Request {
56            kind: T::kind(),
57            version: OptionalVersion {
58                major: T::major(),
59                minor: None,
60            },
61        });
62        self
63    }
64
65    pub fn add_request_exact<T: objects::Object>(&mut self, minor: u32) -> &mut Self {
67        self.query.requests.push(Request {
68            kind: T::kind(),
69            version: OptionalVersion {
70                major: T::major(),
71                minor: Some(minor),
72            },
73        });
74        self
75    }
76
77    pub fn request_all_objects(&mut self) -> &mut Self {
79        self.request_object::<objects::CodeModelV2>()
80            .request_object::<objects::ConfigureLogV1>()
81            .request_object::<objects::CacheV2>()
82            .request_object::<objects::ToolchainsV1>()
83            .request_object::<objects::CMakeFilesV1>()
84    }
85
86    pub fn set_client(&mut self, client_name: &str, client_data: serde_json::Value) -> &mut Self {
94        self.query.client = Some(client_data);
95        self.client_name = Some(client_name.to_owned());
96        self
97    }
98
99    pub fn write_stateless<P: AsRef<Path>>(&self, build_dir: P) -> Result<(), WriterError> {
107        let query_dir = dir(build_dir);
108
109        fs::create_dir_all(&query_dir)?;
111
112        for obj in &self.query.requests {
113            let query_file =
114                query_dir.join(format!("{}-v{}", obj.kind.as_str(), obj.version.major));
115            fs::write(&query_file, "")?;
116        }
117
118        Ok(())
119    }
120
121    pub fn write_stateful<P: AsRef<Path>>(&self, build_dir: P) -> Result<(), WriterError> {
132        let query_dir = dir(build_dir);
133        let client_dir = query_dir.join(
134            self.client_name
135                .as_ref()
136                .ok_or(WriterError::ClientNameNotSet)?,
137        );
138
139        fs::create_dir_all(&client_dir)?;
141
142        let query_file = client_dir.join("query.json");
144        let query = serde_json::to_string(&self.query)?;
145        fs::write(query_file, query)?;
146
147        Ok(())
148    }
149}
150
151#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
152struct OptionalVersion {
153    major: u32,
154    #[serde(skip_serializing_if = "Option::is_none")]
155    minor: Option<u32>,
156}
157
158#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
159struct Request {
160    kind: ObjectKind,
161    version: OptionalVersion,
162}
163
164#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
165struct Query {
166    requests: Vec<Request>,
167    client: Option<serde_json::Value>,
168}
169pub fn dir<P: AsRef<Path>>(build_dir: P) -> PathBuf {
171    Path::new(build_dir.as_ref())
172        .join(".cmake")
173        .join("api")
174        .join("v1")
175        .join("query")
176}