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