typescript_tools/
configuration_file.rs

1use std::{
2    fmt::Display,
3    fs::File,
4    io::{self, BufWriter, Write},
5    path::{Path, PathBuf},
6};
7
8use serde::Serialize;
9
10use crate::{io::FromFileError, types::Directory};
11
12#[derive(Debug)]
13#[non_exhaustive]
14pub struct WriteError {
15    pub path: PathBuf,
16    pub kind: WriteErrorKind,
17}
18
19impl Display for WriteError {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        write!(f, "unable to write file {:?}", self.path)
22    }
23}
24
25impl std::error::Error for WriteError {
26    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
27        match &self.kind {
28            WriteErrorKind::OpenFile(err) => Some(err),
29            WriteErrorKind::Serialize(err) => Some(err),
30            WriteErrorKind::Stream(err) => Some(err),
31        }
32    }
33}
34
35#[derive(Debug)]
36pub enum WriteErrorKind {
37    OpenFile(io::Error),
38    Serialize(serde_json::Error),
39    Stream(io::Error),
40}
41
42// REFACTOR: most of this impl is the same across all types
43/// Configuration file for some component of the monorepo.
44pub trait ConfigurationFile: Sized {
45    type Contents: Serialize;
46
47    /// Basename of the configuration file.
48    const FILENAME: &'static str;
49
50    /// Create an instance of this configuration file by reading
51    /// the specified file from this directory on disk.
52    fn from_directory(
53        monorepo_root: &Directory,
54        relative_directory: Directory,
55    ) -> Result<Self, FromFileError>;
56
57    /// Relative path to directory containing this configuration file,
58    /// from monorepo root.
59    fn directory(&self) -> &Directory;
60
61    /// Relative path to this configuration file from the monorepo root.
62    fn path(&self) -> PathBuf;
63
64    fn contents(&self) -> &Self::Contents;
65
66    fn write(
67        monorepo_root: &Path,
68        configuration_file: impl ConfigurationFile,
69    ) -> Result<(), WriteError> {
70        let filename = monorepo_root.join(configuration_file.path());
71        let file = File::create(filename.clone()).map_err(|err| WriteError {
72            path: filename.clone(),
73            kind: WriteErrorKind::OpenFile(err),
74        })?;
75        let mut writer = BufWriter::new(file);
76        (|| {
77            let s = serde_json::to_string_pretty(configuration_file.contents())
78                .map_err(WriteErrorKind::Serialize)?;
79            writeln!(writer, "{}", s).map_err(WriteErrorKind::Stream)
80        })()
81        .map_err(|kind| WriteError {
82            path: filename,
83            kind,
84        })?;
85        Ok(())
86    }
87}