Skip to main content

xray_tui/
config.rs

1use std::path::{Path, PathBuf};
2
3use anyhow::Context;
4use clap::Parser;
5use dirs::{config_dir, state_dir};
6
7#[derive(clap::Args)]
8#[group(required = false, multiple = false)]
9struct ClapImageSource {
10    /// Force image resolution using Docker.
11    #[arg(short = 'd', long = "docker")]
12    force_docker: bool,
13    /// Force image resolution using a tarred image.
14    #[arg(short = 'f', long = "fs")]
15    force_fs: bool,
16    /// Force image resolution using Podman.
17    #[arg(long = "podman")]
18    force_podman: bool,
19}
20
21impl ClapImageSource {
22    fn into_enum(self) -> ImageSource {
23        if self.force_docker {
24            ImageSource::ForceDocker
25        } else if self.force_fs {
26            ImageSource::ForceFS
27        } else if self.force_podman {
28            ImageSource::ForcePodman
29        } else {
30            ImageSource::Default
31        }
32    }
33}
34
35#[derive(Parser)]
36#[command(version, about)]
37struct Arg {
38    /// Override the config directory location.
39    ///
40    /// Default: $XDG_CONFIG_HOME/xray or $HOME/.config/xray
41    #[arg(short = 'p', long)]
42    config_path: Option<PathBuf>,
43    // TODO: implement layer caching
44    // #[arg(short = 'c', long, default_value_t = true)]
45    // cache_layers: bool,
46    #[clap(flatten)]
47    image_source: ClapImageSource,
48    #[arg()]
49    image: String,
50}
51
52/// Used to configure the provided image's source.
53#[derive(Debug, Clone, Copy)]
54pub enum ImageSource {
55    /// Try to read to read the image from FS, then Docker, then Podman.
56    Default,
57    /// Try Docker, don't try reading the image from anywhere else.
58    ForceDocker,
59    /// Try to read the image from FS, don't try reading the image from anywhere else.
60    ForceFS,
61    /// Try Podman, don't try reading the image from anywhere else.
62    ForcePodman,
63}
64
65#[derive(Debug)]
66pub struct Config {
67    config_path: PathBuf,
68    state_path: PathBuf,
69    image: String,
70    image_source: ImageSource,
71}
72
73impl Config {
74    pub fn new() -> anyhow::Result<Self> {
75        let Arg {
76            config_path,
77            image,
78            image_source,
79        } = Arg::parse();
80        let image_source = image_source.into_enum();
81
82        let config_path = config_path
83            .or_else(default_config_path)
84            .context("failed to get the config directory")?;
85        let state_path = default_state_path()
86            .or_else(|| Some(config_path.clone()))
87            .context("failed to get the state directory")?;
88
89        std::fs::create_dir_all(&config_path)
90            .context("failed to create the config directory")?;
91        std::fs::create_dir_all(&state_path)
92            .context("failed to create the state directory")?;
93
94        Ok(Config {
95            config_path,
96            state_path,
97            image,
98            image_source,
99        })
100    }
101
102    pub fn make_config_path(&self, path: impl AsRef<Path>) -> PathBuf {
103        let mut config_path = self.config_path.clone();
104        config_path.push(path.as_ref());
105        config_path
106    }
107
108    pub fn config_path(&self) -> &Path {
109        &self.config_path
110    }
111
112    pub fn state_path(&self) -> &Path {
113        &self.state_path
114    }
115
116    pub fn image(&self) -> &str {
117        &self.image
118    }
119
120    pub fn image_source(&self) -> ImageSource {
121        self.image_source
122    }
123}
124
125fn default_config_path() -> Option<PathBuf> {
126    config_dir().map(|mut path| {
127        path.push("xray");
128        path
129    })
130}
131
132fn default_state_path() -> Option<PathBuf> {
133    state_dir().map(|mut path| {
134        path.push("xray");
135        path
136    })
137}