nix_compat/nixcpp/
conf.rs1use std::{fmt::Display, str::FromStr};
2
3#[derive(Clone, Debug, Default, Eq, PartialEq)]
6pub struct NixConfig<'a> {
7 pub allowed_users: Option<Vec<&'a str>>,
8 pub auto_optimise_store: Option<bool>,
9 pub cores: Option<u64>,
10 pub max_jobs: Option<u64>,
11 pub require_sigs: Option<bool>,
12 pub sandbox: Option<SandboxSetting>,
13 pub sandbox_fallback: Option<bool>,
14 pub substituters: Option<Vec<&'a str>>,
15 pub system_features: Option<Vec<&'a str>>,
16 pub trusted_public_keys: Option<Vec<crate::narinfo::VerifyingKey>>,
17 pub trusted_substituters: Option<Vec<&'a str>>,
18 pub trusted_users: Option<Vec<&'a str>>,
19 pub extra_platforms: Option<Vec<&'a str>>,
20 pub extra_sandbox_paths: Option<Vec<&'a str>>,
21 pub experimental_features: Option<Vec<&'a str>>,
22 pub builders_use_substitutes: Option<bool>,
23}
24
25impl<'a> NixConfig<'a> {
26 pub fn parse(input: &'a str) -> Result<Self, Error> {
31 let mut out = Self::default();
32
33 for line in input.lines() {
34 let line = if let Some((line, _comment)) = line.split_once('#') {
36 line
37 } else {
38 line
39 };
40
41 if line.trim().is_empty() {
43 continue;
44 }
45
46 let (tag, val) = line
47 .split_once('=')
48 .ok_or_else(|| Error::InvalidLine(line.to_string()))?;
49
50 let tag = tag.trim();
52 let val = val.trim();
53
54 #[inline]
55 fn parse_val<'a>(this: &mut NixConfig<'a>, tag: &str, val: &'a str) -> Option<()> {
56 match tag {
57 "allowed-users" => {
58 this.allowed_users = Some(val.split_whitespace().collect());
59 }
60 "auto-optimise-store" => {
61 this.auto_optimise_store = Some(val.parse::<bool>().ok()?);
62 }
63 "cores" => {
64 this.cores = Some(val.parse().ok()?);
65 }
66 "max-jobs" => {
67 this.max_jobs = Some(val.parse().ok()?);
68 }
69 "require-sigs" => {
70 this.require_sigs = Some(val.parse().ok()?);
71 }
72 "sandbox" => this.sandbox = Some(val.parse().ok()?),
73 "sandbox-fallback" => this.sandbox_fallback = Some(val.parse().ok()?),
74 "substituters" => this.substituters = Some(val.split_whitespace().collect()),
75 "system-features" => {
76 this.system_features = Some(val.split_whitespace().collect())
77 }
78 "trusted-public-keys" => {
79 this.trusted_public_keys = Some(
80 val.split_whitespace()
81 .map(crate::narinfo::VerifyingKey::parse)
82 .collect::<Result<Vec<crate::narinfo::VerifyingKey>, _>>()
83 .ok()?,
84 )
85 }
86 "trusted-substituters" => {
87 this.trusted_substituters = Some(val.split_whitespace().collect())
88 }
89 "trusted-users" => this.trusted_users = Some(val.split_whitespace().collect()),
90 "extra-platforms" => {
91 this.extra_platforms = Some(val.split_whitespace().collect())
92 }
93 "extra-sandbox-paths" => {
94 this.extra_sandbox_paths = Some(val.split_whitespace().collect())
95 }
96 "experimental-features" => {
97 this.experimental_features = Some(val.split_whitespace().collect())
98 }
99 "builders-use-substitutes" => {
100 this.builders_use_substitutes = Some(val.parse().ok()?)
101 }
102 _ => return None,
103 }
104 Some(())
105 }
106
107 parse_val(&mut out, tag, val)
108 .ok_or_else(|| Error::InvalidValue(tag.to_string(), val.to_string()))?
109 }
110
111 Ok(out)
112 }
113}
114
115#[derive(thiserror::Error, Debug)]
116pub enum Error {
117 #[error("Invalid line: {0}")]
118 InvalidLine(String),
119 #[error("Unrecognized key: {0}")]
120 UnrecognizedKey(String),
121 #[error("Invalid value '{1}' for key '{0}'")]
122 InvalidValue(String, String),
123}
124
125#[derive(Clone, Debug, PartialEq, Eq)]
127pub enum SandboxSetting {
128 True,
129 False,
130 Relaxed,
131}
132
133impl Display for SandboxSetting {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 match self {
136 SandboxSetting::True => write!(f, "true"),
137 SandboxSetting::False => write!(f, "false"),
138 SandboxSetting::Relaxed => write!(f, "relaxed"),
139 }
140 }
141}
142
143impl FromStr for SandboxSetting {
144 type Err = &'static str;
145
146 fn from_str(s: &str) -> Result<Self, Self::Err> {
147 Ok(match s {
148 "true" => Self::True,
149 "false" => Self::False,
150 "relaxed" => Self::Relaxed,
151 _ => return Err("invalid value"),
152 })
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use crate::{narinfo::VerifyingKey, nixcpp::conf::SandboxSetting};
159
160 use super::NixConfig;
161
162 #[test]
163 pub fn test_parse() {
164 let config = NixConfig::parse(include_str!("../../testdata/nix.conf")).expect("must parse");
165
166 assert_eq!(
167 NixConfig {
168 allowed_users: Some(vec!["*"]),
169 auto_optimise_store: Some(false),
170 cores: Some(0),
171 max_jobs: Some(8),
172 require_sigs: Some(true),
173 sandbox: Some(SandboxSetting::True),
174 sandbox_fallback: Some(false),
175 substituters: Some(vec!["https://nix-community.cachix.org", "https://cache.nixos.org/"]),
176 system_features: Some(vec!["nixos-test", "benchmark", "big-parallel", "kvm"]),
177 trusted_public_keys: Some(vec![
178 VerifyingKey::parse("cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=")
179 .expect("failed to parse pubkey"),
180 VerifyingKey::parse("nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=")
181 .expect("failed to parse pubkey")
182 ]),
183 trusted_substituters: Some(vec![]),
184 trusted_users: Some(vec!["flokli"]),
185 extra_platforms: Some(vec!["aarch64-linux", "i686-linux"]),
186 extra_sandbox_paths: Some(vec![
187 "/run/binfmt", "/nix/store/swwyxyqpazzvbwx8bv40z7ih144q841f-qemu-aarch64-binfmt-P-x86_64-unknown-linux-musl"
188 ]),
189 experimental_features: Some(vec!["nix-command"]),
190 builders_use_substitutes: Some(true)
191 },
192 config
193 );
194
195 let other_config = NixConfig::parse(include_str!("../../testdata/other_nix.conf"))
198 .expect("other config must parse");
199
200 assert_eq!(config, other_config);
201 }
202}