etc_os_release/
construct.rs1use std::{
2 convert::Infallible,
3 fs::File,
4 io::{self, BufRead, BufReader},
5 path::{Path, PathBuf},
6 str::FromStr,
7};
8
9use crate::{entry::OsReleaseLine, OsRelease, OsReleaseEntry};
10
11#[derive(Debug, thiserror::Error)]
13#[non_exhaustive]
14pub enum Error {
15 #[error("no os-release file found")]
17 NoOsRelease,
18 #[error("failed to open os-release file: {err:?}")]
20 Open {
21 path: PathBuf,
23 #[source]
25 err: std::io::Error,
26 },
27 #[error("failed to read os-release file: {err:?}")]
29 Read {
30 #[source]
32 err: std::io::Error,
33 },
34}
35
36impl OsRelease {
38 pub fn open() -> Result<Self, Error> {
46 let path = os_release_path().ok_or(Error::NoOsRelease)?;
47 let file = File::open(path).map_err(|err| Error::Open {
48 path: path.to_owned(),
49 err,
50 })?;
51 Self::from_reader(file)
52 }
53
54 pub fn from_reader(reader: impl io::Read) -> Result<Self, Error> {
58 let reader = BufReader::new(reader);
59 reader
60 .lines()
61 .collect::<Result<_, _>>()
62 .map_err(|err| Error::Read { err })
63 }
64}
65
66impl<'a> FromIterator<OsReleaseEntry<'a>> for OsRelease {
67 fn from_iter<T>(iter: T) -> Self
68 where
69 T: IntoIterator<Item = OsReleaseEntry<'a>>,
70 {
71 Self {
72 fields: iter
73 .into_iter()
74 .map(|entry| (entry.key().to_owned(), entry.value().to_owned()))
75 .collect(),
76 }
77 }
78}
79
80impl<'a> FromIterator<&'a str> for OsRelease {
81 fn from_iter<T>(iter: T) -> Self
82 where
83 T: IntoIterator<Item = &'a str>,
84 {
85 iter.into_iter()
86 .filter_map(|line| {
87 OsReleaseLine::from_str(line)
88 .ok()
89 .and_then(|line| line.into_entry())
90 })
91 .collect()
92 }
93}
94
95impl FromIterator<String> for OsRelease {
96 fn from_iter<T>(iter: T) -> Self
97 where
98 T: IntoIterator<Item = String>,
99 {
100 iter.into_iter()
101 .filter_map(|line| {
102 OsReleaseLine::from_str(&line)
103 .ok()
104 .and_then(|line| line.into_entry())
105 })
106 .collect()
107 }
108}
109
110impl FromStr for OsRelease {
111 type Err = Infallible;
112
113 fn from_str(s: &str) -> Result<Self, Self::Err> {
114 Ok(s.lines().collect())
115 }
116}
117
118fn os_release_path() -> Option<&'static Path> {
120 [
121 Path::new("/etc/os-release"),
122 Path::new("/usr/lib/os-release"),
123 ]
124 .into_iter()
125 .find(|path| path.exists())
126}