1#![allow(clippy::unnecessary_unwrap)]
8
9use std::collections::HashMap;
10use std::fmt;
11use std::fs::{self, File};
12use std::io::{BufRead, BufReader, Read, Write};
13use std::path::{Path, PathBuf};
14use std::str::FromStr;
15
16macro_rules! update_and_test {
17 ($self: ident, $set_func:ident, $value:expr, $get_func:ident) => {
18 if let Some(v) = $value {
19 $self.$set_func(v)?;
20 if $self.$get_func()? != v {
21 return Err(Error::new(Other));
22 }
23 }
24 };
25}
26
27macro_rules! update {
28 ($self: ident, $set_func:ident, $value:expr) => {
29 if let Some(v) = $value {
30 let _ = $self.$set_func(v);
31 }
32 };
33}
34
35pub mod blkio;
36pub mod cgroup;
37pub mod cgroup_builder;
38pub mod cpu;
39pub mod cpuacct;
40pub mod cpuset;
41pub mod devices;
42pub mod error;
43pub mod events;
44pub mod freezer;
45pub mod hierarchies;
46pub mod hugetlb;
47pub mod memory;
48pub mod net_cls;
49pub mod net_prio;
50pub mod perf_event;
51pub mod pid;
52pub mod rdma;
53pub mod systemd;
54
55use crate::fs::blkio::BlkIoController;
56use crate::fs::cpu::CpuController;
57use crate::fs::cpuacct::CpuAcctController;
58use crate::fs::cpuset::CpuSetController;
59use crate::fs::devices::DevicesController;
60use crate::fs::error::ErrorKind::*;
61use crate::fs::error::*;
62use crate::fs::freezer::FreezerController;
63use crate::fs::hugetlb::HugeTlbController;
64use crate::fs::memory::MemController;
65use crate::fs::net_cls::NetClsController;
66use crate::fs::net_prio::NetPrioController;
67use crate::fs::perf_event::PerfEventController;
68use crate::fs::pid::PidController;
69use crate::fs::rdma::RdmaController;
70use crate::fs::systemd::SystemdController;
71
72#[doc(inline)]
73pub use crate::fs::cgroup::Cgroup;
74
75#[derive(Debug, Clone)]
77pub enum Subsystem {
78 Pid(PidController),
80 Mem(MemController),
82 CpuSet(CpuSetController),
84 CpuAcct(CpuAcctController),
86 Cpu(CpuController),
88 Devices(DevicesController),
90 Freezer(FreezerController),
92 NetCls(NetClsController),
94 BlkIo(BlkIoController),
96 PerfEvent(PerfEventController),
98 NetPrio(NetPrioController),
100 HugeTlb(HugeTlbController),
102 Rdma(RdmaController),
104 Systemd(SystemdController),
106}
107
108#[doc(hidden)]
109#[derive(Eq, PartialEq, Debug, Clone)]
110pub enum Controllers {
111 Pids,
112 Mem,
113 CpuSet,
114 CpuAcct,
115 Cpu,
116 Devices,
117 Freezer,
118 NetCls,
119 BlkIo,
120 PerfEvent,
121 NetPrio,
122 HugeTlb,
123 Rdma,
124 Systemd,
125}
126
127impl fmt::Display for Controllers {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 match self {
130 Controllers::Pids => write!(f, "pids"),
131 Controllers::Mem => write!(f, "memory"),
132 Controllers::CpuSet => write!(f, "cpuset"),
133 Controllers::CpuAcct => write!(f, "cpuacct"),
134 Controllers::Cpu => write!(f, "cpu"),
135 Controllers::Devices => write!(f, "devices"),
136 Controllers::Freezer => write!(f, "freezer"),
137 Controllers::NetCls => write!(f, "net_cls"),
138 Controllers::BlkIo => write!(f, "blkio"),
139 Controllers::PerfEvent => write!(f, "perf_event"),
140 Controllers::NetPrio => write!(f, "net_prio"),
141 Controllers::HugeTlb => write!(f, "hugetlb"),
142 Controllers::Rdma => write!(f, "rdma"),
143 Controllers::Systemd => write!(f, "name=systemd"),
144 }
145 }
146}
147
148mod sealed {
149 use super::*;
150
151 pub trait ControllerInternal {
152 fn apply(&self, res: &Resources) -> Result<()>;
153
154 fn control_type(&self) -> Controllers;
156 fn get_path(&self) -> &PathBuf;
157 fn get_path_mut(&mut self) -> &mut PathBuf;
158 fn get_base(&self) -> &PathBuf;
159
160 fn post_create(&self) {}
162
163 fn is_v2(&self) -> bool {
164 false
165 }
166
167 fn open_path(&self, p: &str, w: bool) -> Result<File> {
168 let mut path = self.get_path().clone();
169 path.push(p);
170
171 if w {
172 match File::create(&path) {
173 Err(e) => Err(Error::with_cause(
174 ErrorKind::WriteFailed(
175 path.display().to_string(),
176 "[CREATE FILE]".to_string(),
177 ),
178 e,
179 )),
180 Ok(file) => Ok(file),
181 }
182 } else {
183 match File::open(&path) {
184 Err(e) => Err(Error::with_cause(
185 ErrorKind::ReadFailed(path.display().to_string()),
186 e,
187 )),
188 Ok(file) => Ok(file),
189 }
190 }
191 }
192
193 fn get_max_value(&self, f: &str) -> Result<MaxValue> {
194 self.open_path(f, false).and_then(|mut file| {
195 let mut string = String::new();
196 let res = file.read_to_string(&mut string);
197 match res {
198 Ok(_) => parse_max_value(&string),
199 Err(e) => Err(Error::with_cause(ReadFailed(f.to_string()), e)),
200 }
201 })
202 }
203
204 #[doc(hidden)]
205 fn path_exists(&self, p: &str) -> bool {
206 std::path::Path::new(p).exists()
207 }
208 }
209
210 pub trait CustomizedAttribute: ControllerInternal {
211 fn set(&self, key: &str, value: &str) -> Result<()> {
212 self.open_path(key, true).and_then(|mut file| {
213 file.write_all(value.as_ref()).map_err(|e| {
214 Error::with_cause(WriteFailed(key.to_string(), value.to_string()), e)
215 })
216 })
217 }
218
219 #[allow(dead_code)]
220 fn get(&self, key: &str) -> Result<String> {
221 self.open_path(key, false).and_then(|mut file: File| {
222 let mut string = String::new();
223 match file.read_to_string(&mut string) {
224 Ok(_) => Ok(string.trim().to_owned()),
225 Err(e) => Err(Error::with_cause(ReadFailed(key.to_string()), e)),
226 }
227 })
228 }
229 }
230}
231
232pub(crate) use crate::fs::sealed::{ControllerInternal, CustomizedAttribute};
233use crate::CgroupPid;
234
235pub trait Controller {
239 #[doc(hidden)]
240 fn control_type(&self) -> Controllers;
241
242 fn path(&self) -> &Path;
244
245 fn base(&self) -> &Path;
247
248 fn apply(&self, res: &Resources) -> Result<()>;
251
252 fn create(&self) -> Result<()>;
254
255 fn exists(&self) -> bool;
257
258 fn set_notify_on_release(&self, enable: bool) -> Result<()>;
260
261 fn set_release_agent(&self, path: &str) -> Result<()>;
263
264 fn delete(&self) -> Result<()>;
266
267 fn add_task(&self, pid: &CgroupPid) -> Result<()>;
269
270 fn add_task_by_tgid(&self, pid: &CgroupPid) -> Result<()>;
272
273 fn set_cgroup_type(&self, cgroup_type: &str) -> Result<()>;
275
276 fn get_cgroup_type(&self) -> Result<String>;
278
279 fn tasks(&self) -> Vec<CgroupPid>;
281
282 fn procs(&self) -> Vec<CgroupPid>;
284
285 fn v2(&self) -> bool;
286}
287
288impl<T> Controller for T
289where
290 T: ControllerInternal,
291{
292 fn control_type(&self) -> Controllers {
293 ControllerInternal::control_type(self)
294 }
295
296 fn path(&self) -> &Path {
297 self.get_path()
298 }
299
300 fn base(&self) -> &Path {
301 self.get_base()
302 }
303
304 fn apply(&self, res: &Resources) -> Result<()> {
307 ControllerInternal::apply(self, res)
308 }
309
310 fn create(&self) -> Result<()> {
312 std::fs::create_dir_all(self.get_path())
313 .map_err(|err| Error::with_cause(ErrorKind::FsError, err))?;
314 self.post_create();
315 Ok(())
316 }
317
318 fn set_notify_on_release(&self, enable: bool) -> Result<()> {
320 if self.is_v2() {
321 return Err(Error::new(ErrorKind::CgroupVersion));
322 }
323 self.open_path("notify_on_release", true)
324 .and_then(|mut file| {
325 write!(file, "{}", enable as i32).map_err(|e| {
326 Error::with_cause(
327 ErrorKind::WriteFailed("notify_on_release".to_string(), enable.to_string()),
328 e,
329 )
330 })
331 })
332 }
333
334 fn set_release_agent(&self, path: &str) -> Result<()> {
336 if self.is_v2() {
337 return Err(Error::new(ErrorKind::CgroupVersion));
338 }
339 self.open_path("release_agent", true).and_then(|mut file| {
340 file.write_all(path.as_bytes()).map_err(|e| {
341 Error::with_cause(
342 ErrorKind::WriteFailed("release_agent".to_string(), path.to_string()),
343 e,
344 )
345 })
346 })
347 }
348 fn exists(&self) -> bool {
350 self.get_path().exists()
351 }
352
353 fn delete(&self) -> Result<()> {
355 if !self.get_path().exists() {
356 return Ok(());
357 }
358
359 let mut delay = std::time::Duration::from_millis(10);
366 let cgroup_path = self.get_path();
367 for _i in 0..4 {
368 if let Ok(()) = remove_dir(cgroup_path) {
369 return Ok(());
370 }
371 std::thread::sleep(delay);
372 delay *= 2;
373 }
374
375 remove_dir(cgroup_path)
376 }
377
378 fn add_task(&self, pid: &CgroupPid) -> Result<()> {
380 let mut file_name = "tasks";
381 if self.is_v2() {
382 file_name = "cgroup.threads";
383 }
384 self.open_path(file_name, true).and_then(|mut file| {
385 file.write_all(pid.pid.to_string().as_ref()).map_err(|e| {
386 Error::with_cause(
387 ErrorKind::WriteFailed(file_name.to_string(), pid.pid.to_string()),
388 e,
389 )
390 })
391 })
392 }
393
394 fn add_task_by_tgid(&self, pid: &CgroupPid) -> Result<()> {
396 let file_name = "cgroup.procs";
397 self.open_path(file_name, true).and_then(|mut file| {
398 file.write_all(pid.pid.to_string().as_ref()).map_err(|e| {
399 Error::with_cause(
400 ErrorKind::WriteFailed(file_name.to_string(), pid.pid.to_string()),
401 e,
402 )
403 })
404 })
405 }
406
407 fn procs(&self) -> Vec<CgroupPid> {
409 let file_name = "cgroup.procs";
410 self.open_path(file_name, false)
411 .map(|file| {
412 let bf = BufReader::new(file);
413 let mut v = Vec::new();
414 for line in bf.lines() {
415 match line {
416 Ok(line) => {
417 let n = line.trim().parse().unwrap_or(0u64);
418 v.push(n);
419 }
420 Err(_) => break,
421 }
422 }
423 v.into_iter().map(CgroupPid::from).collect()
424 })
425 .unwrap_or_default()
426 }
427
428 fn tasks(&self) -> Vec<CgroupPid> {
430 let mut file_name = "tasks";
431 if self.is_v2() {
432 file_name = "cgroup.threads";
433 }
434 self.open_path(file_name, false)
435 .map(|file| {
436 let bf = BufReader::new(file);
437 let mut v = Vec::new();
438 for line in bf.lines() {
439 match line {
440 Ok(line) => {
441 let n = line.trim().parse().unwrap_or(0u64);
442 v.push(n);
443 }
444 Err(_) => break,
445 }
446 }
447 v.into_iter().map(CgroupPid::from).collect()
448 })
449 .unwrap_or_default()
450 }
451
452 fn set_cgroup_type(&self, cgroup_type: &str) -> Result<()> {
454 if !self.is_v2() {
455 return Err(Error::new(ErrorKind::CgroupVersion));
456 }
457 let file_name = "cgroup.type";
458 self.open_path(file_name, true).and_then(|mut file| {
459 file.write_all(cgroup_type.as_bytes()).map_err(|e| {
460 Error::with_cause(
461 ErrorKind::WriteFailed(file_name.to_string(), cgroup_type.to_string()),
462 e,
463 )
464 })
465 })
466 }
467
468 fn get_cgroup_type(&self) -> Result<String> {
470 if !self.is_v2() {
471 return Err(Error::new(ErrorKind::CgroupVersion));
472 }
473 let file_name = "cgroup.type";
474 self.open_path(file_name, false).and_then(|mut file: File| {
475 let mut string = String::new();
476 match file.read_to_string(&mut string) {
477 Ok(_) => Ok(string.trim().to_owned()),
478 Err(e) => Err(Error::with_cause(
479 ErrorKind::ReadFailed(file_name.to_string()),
480 e,
481 )),
482 }
483 })
484 }
485
486 fn v2(&self) -> bool {
487 self.is_v2()
488 }
489}
490
491fn remove_dir(dir: &Path) -> Result<()> {
494 if fs::remove_dir(dir).is_ok() {
496 return Ok(());
497 }
498
499 if dir.exists() && dir.is_dir() {
500 for entry in fs::read_dir(dir)
501 .map_err(|e| Error::with_cause(ReadFailed(dir.display().to_string()), e))?
502 {
503 let entry =
504 entry.map_err(|e| Error::with_cause(ReadFailed(dir.display().to_string()), e))?;
505 let path = entry.path();
506 if path.is_dir() {
507 remove_dir(&path)?;
508 }
509 }
510 fs::remove_dir(dir).map_err(|e| Error::with_cause(RemoveFailed, e))?;
511 }
512
513 Ok(())
514}
515
516#[doc(hidden)]
517pub trait ControllIdentifier {
518 fn controller_type() -> Controllers;
519}
520
521pub trait Hierarchy: std::fmt::Debug + Send + Sync {
524 fn subsystems(&self) -> Vec<Subsystem>;
526
527 fn root(&self) -> PathBuf;
529
530 fn root_control_group(&self) -> Cgroup;
532
533 fn parent_control_group(&self, path: &str) -> Cgroup;
535
536 fn v2(&self) -> bool;
537}
538
539#[derive(Debug, Clone, Eq, PartialEq, Default)]
541#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
542pub struct MemoryResources {
543 pub kernel_memory_limit: Option<i64>,
545 pub memory_hard_limit: Option<i64>,
547 pub memory_soft_limit: Option<i64>,
550 pub kernel_tcp_memory_limit: Option<i64>,
552 pub memory_swap_limit: Option<i64>,
554 pub swappiness: Option<u64>,
560 pub attrs: HashMap<String, String>,
569}
570
571#[derive(Debug, Clone, Eq, PartialEq, Default)]
573#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
574pub struct PidResources {
575 pub maximum_number_of_processes: Option<MaxValue>,
581}
582
583#[derive(Debug, Clone, Eq, PartialEq, Default)]
585#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
586pub struct CpuResources {
587 pub cpus: Option<String>,
591 pub mems: Option<String>,
594 pub shares: Option<u64>,
598 pub quota: Option<i64>,
600 pub period: Option<u64>,
602 pub realtime_runtime: Option<i64>,
604 pub realtime_period: Option<u64>,
606 pub attrs: HashMap<String, String>,
614}
615
616#[derive(Debug, Clone, Eq, PartialEq, Default)]
618#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
619pub struct DeviceResource {
620 pub allow: bool,
622 pub devtype: crate::fs::devices::DeviceType,
624 pub major: i64,
626 pub minor: i64,
628 pub access: Vec<crate::fs::devices::DevicePermissions>,
630}
631
632#[derive(Debug, Clone, Eq, PartialEq, Default)]
634#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
635pub struct DeviceResources {
636 pub devices: Vec<DeviceResource>,
638}
639
640#[derive(Debug, Clone, Eq, PartialEq, Default)]
642#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
643pub struct NetworkPriority {
644 pub name: String,
646 pub priority: u64,
648}
649
650#[derive(Debug, Clone, Eq, PartialEq, Default)]
653#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
654pub struct NetworkResources {
655 pub class_id: Option<u64>,
659 pub priorities: Vec<NetworkPriority>,
661}
662
663#[derive(Debug, Clone, Eq, PartialEq, Default)]
665#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
666pub struct HugePageResource {
667 pub size: String,
669 pub limit: u64,
672}
673
674#[derive(Debug, Clone, Eq, PartialEq, Default)]
676#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
677pub struct HugePageResources {
678 pub limits: Vec<HugePageResource>,
680}
681
682#[derive(Debug, Clone, Eq, PartialEq, Default)]
684#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
685pub struct BlkIoDeviceResource {
686 pub major: u64,
688 pub minor: u64,
690 pub weight: Option<u16>,
692 pub leaf_weight: Option<u16>,
694}
695
696#[derive(Debug, Clone, Eq, PartialEq, Default)]
698#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
699pub struct BlkIoDeviceThrottleResource {
700 pub major: u64,
702 pub minor: u64,
704 pub rate: u64,
706}
707
708#[derive(Debug, Clone, Eq, PartialEq, Default)]
710#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
711pub struct BlkIoResources {
712 pub weight: Option<u16>,
714 pub leaf_weight: Option<u16>,
716 pub weight_device: Vec<BlkIoDeviceResource>,
718 pub throttle_read_bps_device: Vec<BlkIoDeviceThrottleResource>,
720 pub throttle_read_iops_device: Vec<BlkIoDeviceThrottleResource>,
722 pub throttle_write_bps_device: Vec<BlkIoDeviceThrottleResource>,
724 pub throttle_write_iops_device: Vec<BlkIoDeviceThrottleResource>,
726
727 pub attrs: HashMap<String, String>,
735}
736
737#[derive(Debug, Clone, Eq, PartialEq, Default)]
739#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
740pub struct Resources {
741 pub memory: MemoryResources,
743 pub pid: PidResources,
745 pub cpu: CpuResources,
747 pub devices: DeviceResources,
749 pub network: NetworkResources,
751 pub hugepages: HugePageResources,
753 pub blkio: BlkIoResources,
755}
756
757impl Subsystem {
758 fn enter(self, path: &Path) -> Self {
759 match self {
760 Subsystem::Pid(mut cont) => Subsystem::Pid({
761 cont.get_path_mut().push(path);
762 cont
763 }),
764 Subsystem::Mem(mut cont) => Subsystem::Mem({
765 cont.get_path_mut().push(path);
766 cont
767 }),
768 Subsystem::CpuSet(mut cont) => Subsystem::CpuSet({
769 cont.get_path_mut().push(path);
770 cont
771 }),
772 Subsystem::CpuAcct(mut cont) => Subsystem::CpuAcct({
773 cont.get_path_mut().push(path);
774 cont
775 }),
776 Subsystem::Cpu(mut cont) => Subsystem::Cpu({
777 cont.get_path_mut().push(path);
778 cont
779 }),
780 Subsystem::Devices(mut cont) => Subsystem::Devices({
781 cont.get_path_mut().push(path);
782 cont
783 }),
784 Subsystem::Freezer(mut cont) => Subsystem::Freezer({
785 cont.get_path_mut().push(path);
786 cont
787 }),
788 Subsystem::NetCls(mut cont) => Subsystem::NetCls({
789 cont.get_path_mut().push(path);
790 cont
791 }),
792 Subsystem::BlkIo(mut cont) => Subsystem::BlkIo({
793 cont.get_path_mut().push(path);
794 cont
795 }),
796 Subsystem::PerfEvent(mut cont) => Subsystem::PerfEvent({
797 cont.get_path_mut().push(path);
798 cont
799 }),
800 Subsystem::NetPrio(mut cont) => Subsystem::NetPrio({
801 cont.get_path_mut().push(path);
802 cont
803 }),
804 Subsystem::HugeTlb(mut cont) => Subsystem::HugeTlb({
805 cont.get_path_mut().push(path);
806 cont
807 }),
808 Subsystem::Rdma(mut cont) => Subsystem::Rdma({
809 cont.get_path_mut().push(path);
810 cont
811 }),
812 Subsystem::Systemd(mut cont) => Subsystem::Systemd({
813 cont.get_path_mut().push(path);
814 cont
815 }),
816 }
817 }
818
819 pub fn to_controller(&self) -> &dyn Controller {
820 match self {
821 Subsystem::Pid(cont) => cont,
822 Subsystem::Mem(cont) => cont,
823 Subsystem::CpuSet(cont) => cont,
824 Subsystem::CpuAcct(cont) => cont,
825 Subsystem::Cpu(cont) => cont,
826 Subsystem::Devices(cont) => cont,
827 Subsystem::Freezer(cont) => cont,
828 Subsystem::NetCls(cont) => cont,
829 Subsystem::BlkIo(cont) => cont,
830 Subsystem::PerfEvent(cont) => cont,
831 Subsystem::NetPrio(cont) => cont,
832 Subsystem::HugeTlb(cont) => cont,
833 Subsystem::Rdma(cont) => cont,
834 Subsystem::Systemd(cont) => cont,
835 }
836 }
837
838 pub fn controller_name(&self) -> String {
839 self.to_controller().control_type().to_string()
840 }
841}
842
843#[derive(Eq, PartialEq, Copy, Clone, Debug)]
845#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
846pub enum MaxValue {
847 Max,
849 Value(i64),
851}
852
853#[allow(clippy::derivable_impls)]
854impl Default for MaxValue {
855 fn default() -> Self {
856 MaxValue::Max
857 }
858}
859
860impl MaxValue {
861 #[allow(clippy::should_implement_trait, clippy::wrong_self_convention)]
862 fn to_i64(&self) -> i64 {
863 match self {
864 MaxValue::Max => -1,
865 MaxValue::Value(num) => *num,
866 }
867 }
868}
869
870impl fmt::Display for MaxValue {
871 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
872 match self {
873 MaxValue::Max => write!(f, "max"),
874 MaxValue::Value(num) => write!(f, "{}", num),
875 }
876 }
877}
878
879pub fn parse_max_value(s: &str) -> Result<MaxValue> {
880 if s.trim() == "max" {
881 return Ok(MaxValue::Max);
882 }
883 match s.trim().parse() {
884 Ok(val) => Ok(MaxValue::Value(val)),
885 Err(e) => Err(Error::with_cause(ParseError, e)),
886 }
887}
888
889pub fn flat_keyed_to_vec(mut file: File) -> Result<Vec<(String, i64)>> {
893 let mut content = String::new();
894 file.read_to_string(&mut content)
895 .map_err(|e| Error::with_cause(ReadFailed("FIXME: read_string_from".to_string()), e))?;
896
897 let mut v = Vec::new();
898 for line in content.lines() {
899 let parts: Vec<&str> = line.split(' ').collect();
900 if parts.len() == 2 {
901 if let Ok(i) = parts[1].parse::<i64>() {
902 v.push((parts[0].to_string(), i));
903 }
904 }
905 }
906 Ok(v)
907}
908
909pub fn flat_keyed_to_hashmap(mut file: File) -> Result<HashMap<String, i64>> {
913 let mut content = String::new();
914 file.read_to_string(&mut content)
915 .map_err(|e| Error::with_cause(ReadFailed("FIXME: read_string_from".to_string()), e))?;
916
917 let mut h = HashMap::new();
918 for line in content.lines() {
919 let parts: Vec<&str> = line.split(' ').collect();
920 if parts.len() == 2 {
921 if let Ok(i) = parts[1].parse::<i64>() {
922 h.insert(parts[0].to_string(), i);
923 }
924 }
925 }
926 Ok(h)
927}
928
929pub fn nested_keyed_to_hashmap(mut file: File) -> Result<HashMap<String, HashMap<String, i64>>> {
933 let mut content = String::new();
934 file.read_to_string(&mut content)
935 .map_err(|e| Error::with_cause(ReadFailed("FIXME: read_string_from".to_string()), e))?;
936
937 let mut h = HashMap::new();
938 for line in content.lines() {
939 let parts: Vec<&str> = line.split(' ').collect();
940 if parts.is_empty() {
941 continue;
942 }
943 let mut th = HashMap::new();
944 for item in parts[1..].iter() {
945 let fields: Vec<&str> = item.split('=').collect();
946 if fields.len() == 2 {
947 if let Ok(i) = fields[1].parse::<i64>() {
948 th.insert(fields[0].to_string(), i);
949 }
950 }
951 }
952 h.insert(parts[0].to_string(), th);
953 }
954
955 Ok(h)
956}
957
958fn read_from<T>(mut file: File) -> Result<T>
959where
960 T: FromStr,
961 <T as FromStr>::Err: 'static + Send + Sync + std::error::Error,
962{
963 let mut string = String::new();
964 match file.read_to_string(&mut string) {
965 Ok(_) => string
966 .trim()
967 .parse::<T>()
968 .map_err(|e| Error::with_cause(ParseError, e)),
969 Err(e) => Err(Error::with_cause(
970 ReadFailed("FIXME: can't get path in fn read_from".to_string()),
971 e,
972 )),
973 }
974}
975
976fn read_string_from(mut file: File) -> Result<String> {
977 let mut string = String::new();
978 match file.read_to_string(&mut string) {
979 Ok(_) => Ok(string.trim().to_string()),
980 Err(e) => Err(Error::with_cause(
981 ReadFailed("FIXME: can't get path in fn read_string_from".to_string()),
982 e,
983 )),
984 }
985}
986
987fn read_u64_from(file: File) -> Result<u64> {
989 read_from::<u64>(file)
990}
991
992fn read_i64_from(file: File) -> Result<i64> {
994 read_from::<i64>(file)
995}