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::blkio::BlkIoController;
57use crate::cpu::CpuController;
58use crate::cpuacct::CpuAcctController;
59use crate::cpuset::CpuSetController;
60use crate::devices::DevicesController;
61use crate::error::ErrorKind::*;
62use crate::error::*;
63use crate::freezer::FreezerController;
64use crate::hugetlb::HugeTlbController;
65use crate::memory::MemController;
66use crate::net_cls::NetClsController;
67use crate::net_prio::NetPrioController;
68use crate::perf_event::PerfEventController;
69use crate::pid::PidController;
70use crate::rdma::RdmaController;
71use crate::systemd::SystemdController;
72
73#[doc(inline)]
74pub use crate::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 fn get(&self, key: &str) -> Result<String> {
235 self.open_path(key, false).and_then(|mut file: File| {
236 let mut string = String::new();
237 match file.read_to_string(&mut string) {
238 Ok(_) => Ok(string.trim().to_owned()),
239 Err(e) => Err(Error::with_cause(ReadFailed(key.to_string()), e)),
240 }
241 })
242 }
243 }
244}
245
246pub(crate) use crate::sealed::{ControllerInternal, CustomizedAttribute};
247
248pub trait Controller {
252 #[doc(hidden)]
253 fn control_type(&self) -> Controllers;
254
255 fn path(&self) -> &Path;
257
258 fn apply(&self, res: &Resources) -> Result<()>;
261
262 fn create(&self);
264
265 fn exists(&self) -> bool;
267
268 fn set_notify_on_release(&self, enable: bool) -> Result<()>;
270
271 fn set_release_agent(&self, path: &str) -> Result<()>;
273
274 fn delete(&self) -> Result<()>;
276
277 fn add_task(&self, pid: &CgroupPid) -> Result<()>;
279
280 fn add_task_by_tgid(&self, pid: &CgroupPid) -> Result<()>;
282
283 fn set_cgroup_type(&self, cgroup_type: &str) -> Result<()>;
285
286 fn get_cgroup_type(&self) -> Result<String>;
288
289 fn tasks(&self) -> Vec<CgroupPid>;
291
292 fn procs(&self) -> Vec<CgroupPid>;
294
295 fn v2(&self) -> bool;
296}
297
298impl<T> Controller for T
299where
300 T: ControllerInternal,
301{
302 fn control_type(&self) -> Controllers {
303 ControllerInternal::control_type(self)
304 }
305
306 fn path(&self) -> &Path {
307 self.get_path()
308 }
309
310 fn apply(&self, res: &Resources) -> Result<()> {
313 ControllerInternal::apply(self, res)
314 }
315
316 fn create(&self) {
318 self.verify_path()
319 .unwrap_or_else(|_| panic!("path should be valid: {:?}", self.path()));
320
321 match ::std::fs::create_dir_all(self.get_path()) {
322 Ok(_) => self.post_create(),
323 Err(e) => warn!("error create_dir: {:?} error: {:?}", self.get_path(), e),
324 }
325 }
326
327 fn set_notify_on_release(&self, enable: bool) -> Result<()> {
329 if self.is_v2() {
330 return Err(Error::new(ErrorKind::CgroupVersion));
331 }
332 self.open_path("notify_on_release", true)
333 .and_then(|mut file| {
334 write!(file, "{}", enable as i32).map_err(|e| {
335 Error::with_cause(
336 ErrorKind::WriteFailed("notify_on_release".to_string(), enable.to_string()),
337 e,
338 )
339 })
340 })
341 }
342
343 fn set_release_agent(&self, path: &str) -> Result<()> {
345 if self.is_v2() {
346 return Err(Error::new(ErrorKind::CgroupVersion));
347 }
348 self.open_path("release_agent", true).and_then(|mut file| {
349 file.write_all(path.as_bytes()).map_err(|e| {
350 Error::with_cause(
351 ErrorKind::WriteFailed("release_agent".to_string(), path.to_string()),
352 e,
353 )
354 })
355 })
356 }
357 fn exists(&self) -> bool {
359 self.get_path().exists()
360 }
361
362 fn delete(&self) -> Result<()> {
364 if !self.get_path().exists() {
365 return Ok(());
366 }
367
368 let mut delay = std::time::Duration::from_millis(10);
375 let cgroup_path = self.get_path();
376 for _i in 0..4 {
377 if let Ok(()) = remove_dir(cgroup_path) {
378 return Ok(());
379 }
380 std::thread::sleep(delay);
381 delay *= 2;
382 }
383
384 remove_dir(cgroup_path)
385 }
386
387 fn add_task(&self, pid: &CgroupPid) -> Result<()> {
389 let mut file_name = "tasks";
390 if self.is_v2() {
391 file_name = "cgroup.threads";
392 }
393 self.open_path(file_name, true).and_then(|mut file| {
394 file.write_all(pid.pid.to_string().as_ref()).map_err(|e| {
395 Error::with_cause(
396 ErrorKind::WriteFailed(file_name.to_string(), pid.pid.to_string()),
397 e,
398 )
399 })
400 })
401 }
402
403 fn add_task_by_tgid(&self, pid: &CgroupPid) -> Result<()> {
405 let file_name = "cgroup.procs";
406 self.open_path(file_name, true).and_then(|mut file| {
407 file.write_all(pid.pid.to_string().as_ref()).map_err(|e| {
408 Error::with_cause(
409 ErrorKind::WriteFailed(file_name.to_string(), pid.pid.to_string()),
410 e,
411 )
412 })
413 })
414 }
415
416 fn procs(&self) -> Vec<CgroupPid> {
418 let file_name = "cgroup.procs";
419 self.open_path(file_name, false)
420 .map(|file| {
421 let bf = BufReader::new(file);
422 let mut v = Vec::new();
423 for line in bf.lines() {
424 match line {
425 Ok(line) => {
426 let n = line.trim().parse().unwrap_or(0u64);
427 v.push(n);
428 }
429 Err(_) => break,
430 }
431 }
432 v.into_iter().map(CgroupPid::from).collect()
433 })
434 .unwrap_or_default()
435 }
436
437 fn tasks(&self) -> Vec<CgroupPid> {
439 let mut file_name = "tasks";
440 if self.is_v2() {
441 file_name = "cgroup.threads";
442 }
443 self.open_path(file_name, false)
444 .map(|file| {
445 let bf = BufReader::new(file);
446 let mut v = Vec::new();
447 for line in bf.lines() {
448 match line {
449 Ok(line) => {
450 let n = line.trim().parse().unwrap_or(0u64);
451 v.push(n);
452 }
453 Err(_) => break,
454 }
455 }
456 v.into_iter().map(CgroupPid::from).collect()
457 })
458 .unwrap_or_default()
459 }
460
461 fn set_cgroup_type(&self, cgroup_type: &str) -> Result<()> {
463 if !self.is_v2() {
464 return Err(Error::new(ErrorKind::CgroupVersion));
465 }
466 let file_name = "cgroup.type";
467 self.open_path(file_name, true).and_then(|mut file| {
468 file.write_all(cgroup_type.as_bytes()).map_err(|e| {
469 Error::with_cause(
470 ErrorKind::WriteFailed(file_name.to_string(), cgroup_type.to_string()),
471 e,
472 )
473 })
474 })
475 }
476
477 fn get_cgroup_type(&self) -> Result<String> {
479 if !self.is_v2() {
480 return Err(Error::new(ErrorKind::CgroupVersion));
481 }
482 let file_name = "cgroup.type";
483 self.open_path(file_name, false).and_then(|mut file: File| {
484 let mut string = String::new();
485 match file.read_to_string(&mut string) {
486 Ok(_) => Ok(string.trim().to_owned()),
487 Err(e) => Err(Error::with_cause(
488 ErrorKind::ReadFailed(file_name.to_string()),
489 e,
490 )),
491 }
492 })
493 }
494
495 fn v2(&self) -> bool {
496 self.is_v2()
497 }
498}
499
500fn remove_dir(dir: &Path) -> Result<()> {
503 if fs::remove_dir(dir).is_ok() {
505 return Ok(());
506 }
507
508 if dir.exists() && dir.is_dir() {
509 for entry in fs::read_dir(dir)
510 .map_err(|e| Error::with_cause(ReadFailed(dir.display().to_string()), e))?
511 {
512 let entry =
513 entry.map_err(|e| Error::with_cause(ReadFailed(dir.display().to_string()), e))?;
514 let path = entry.path();
515 if path.is_dir() {
516 remove_dir(&path)?;
517 }
518 }
519 fs::remove_dir(dir).map_err(|e| Error::with_cause(RemoveFailed, e))?;
520 }
521
522 Ok(())
523}
524
525#[doc(hidden)]
526pub trait ControllIdentifier {
527 fn controller_type() -> Controllers;
528}
529
530pub trait Hierarchy: std::fmt::Debug + Send + Sync {
533 fn subsystems(&self) -> Vec<Subsystem>;
535
536 fn root(&self) -> PathBuf;
538
539 fn root_control_group(&self) -> Cgroup;
541
542 fn parent_control_group(&self, path: &str) -> Cgroup;
544
545 fn v2(&self) -> bool;
546}
547
548#[derive(Debug, Clone, Eq, PartialEq, Default)]
550#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
551pub struct MemoryResources {
552 pub kernel_memory_limit: Option<i64>,
554 pub memory_hard_limit: Option<i64>,
556 pub memory_soft_limit: Option<i64>,
559 pub kernel_tcp_memory_limit: Option<i64>,
561 pub memory_swap_limit: Option<i64>,
563 pub swappiness: Option<u64>,
569 pub attrs: HashMap<String, String>,
578}
579
580#[derive(Debug, Clone, Eq, PartialEq, Default)]
582#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
583pub struct PidResources {
584 pub maximum_number_of_processes: Option<MaxValue>,
590}
591
592#[derive(Debug, Clone, Eq, PartialEq, Default)]
594#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
595pub struct CpuResources {
596 pub cpus: Option<String>,
600 pub mems: Option<String>,
603 pub shares: Option<u64>,
607 pub quota: Option<i64>,
609 pub period: Option<u64>,
611 pub realtime_runtime: Option<i64>,
613 pub realtime_period: Option<u64>,
615 pub attrs: HashMap<String, String>,
623}
624
625#[derive(Debug, Clone, Eq, PartialEq, Default)]
627#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
628pub struct DeviceResource {
629 pub allow: bool,
631 pub devtype: crate::devices::DeviceType,
633 pub major: i64,
635 pub minor: i64,
637 pub access: Vec<crate::devices::DevicePermissions>,
639}
640
641#[derive(Debug, Clone, Eq, PartialEq, Default)]
643#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
644pub struct DeviceResources {
645 pub devices: Vec<DeviceResource>,
647}
648
649#[derive(Debug, Clone, Eq, PartialEq, Default)]
651#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
652pub struct NetworkPriority {
653 pub name: String,
655 pub priority: u64,
657}
658
659#[derive(Debug, Clone, Eq, PartialEq, Default)]
662#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
663pub struct NetworkResources {
664 pub class_id: Option<u64>,
668 pub priorities: Vec<NetworkPriority>,
670}
671
672#[derive(Debug, Clone, Eq, PartialEq, Default)]
674#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
675pub struct HugePageResource {
676 pub size: String,
678 pub limit: u64,
681}
682
683#[derive(Debug, Clone, Eq, PartialEq, Default)]
685#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
686pub struct HugePageResources {
687 pub limits: Vec<HugePageResource>,
689}
690
691#[derive(Debug, Clone, Eq, PartialEq, Default)]
693#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
694pub struct BlkIoDeviceResource {
695 pub major: u64,
697 pub minor: u64,
699 pub weight: Option<u16>,
701 pub leaf_weight: Option<u16>,
703}
704
705#[derive(Debug, Clone, Eq, PartialEq, Default)]
707#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
708pub struct BlkIoDeviceThrottleResource {
709 pub major: u64,
711 pub minor: u64,
713 pub rate: u64,
715}
716
717#[derive(Debug, Clone, Eq, PartialEq, Default)]
719#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
720pub struct BlkIoResources {
721 pub weight: Option<u16>,
723 pub leaf_weight: Option<u16>,
725 pub weight_device: Vec<BlkIoDeviceResource>,
727 pub throttle_read_bps_device: Vec<BlkIoDeviceThrottleResource>,
729 pub throttle_read_iops_device: Vec<BlkIoDeviceThrottleResource>,
731 pub throttle_write_bps_device: Vec<BlkIoDeviceThrottleResource>,
733 pub throttle_write_iops_device: Vec<BlkIoDeviceThrottleResource>,
735
736 pub attrs: HashMap<String, String>,
744}
745
746#[derive(Debug, Clone, Eq, PartialEq, Default)]
748#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
749pub struct Resources {
750 pub memory: MemoryResources,
752 pub pid: PidResources,
754 pub cpu: CpuResources,
756 pub devices: DeviceResources,
758 pub network: NetworkResources,
760 pub hugepages: HugePageResources,
762 pub blkio: BlkIoResources,
764}
765
766#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
769pub struct CgroupPid {
770 pub pid: u64,
772}
773
774impl From<u64> for CgroupPid {
775 fn from(u: u64) -> CgroupPid {
776 CgroupPid { pid: u }
777 }
778}
779
780impl<'a> From<&'a std::process::Child> for CgroupPid {
781 fn from(u: &std::process::Child) -> CgroupPid {
782 CgroupPid { pid: u.id() as u64 }
783 }
784}
785
786impl Subsystem {
787 fn enter(self, path: &Path) -> Self {
788 match self {
789 Subsystem::Pid(mut cont) => Subsystem::Pid({
790 cont.get_path_mut().push(path);
791 cont
792 }),
793 Subsystem::Mem(mut cont) => Subsystem::Mem({
794 cont.get_path_mut().push(path);
795 cont
796 }),
797 Subsystem::CpuSet(mut cont) => Subsystem::CpuSet({
798 cont.get_path_mut().push(path);
799 cont
800 }),
801 Subsystem::CpuAcct(mut cont) => Subsystem::CpuAcct({
802 cont.get_path_mut().push(path);
803 cont
804 }),
805 Subsystem::Cpu(mut cont) => Subsystem::Cpu({
806 cont.get_path_mut().push(path);
807 cont
808 }),
809 Subsystem::Devices(mut cont) => Subsystem::Devices({
810 cont.get_path_mut().push(path);
811 cont
812 }),
813 Subsystem::Freezer(mut cont) => Subsystem::Freezer({
814 cont.get_path_mut().push(path);
815 cont
816 }),
817 Subsystem::NetCls(mut cont) => Subsystem::NetCls({
818 cont.get_path_mut().push(path);
819 cont
820 }),
821 Subsystem::BlkIo(mut cont) => Subsystem::BlkIo({
822 cont.get_path_mut().push(path);
823 cont
824 }),
825 Subsystem::PerfEvent(mut cont) => Subsystem::PerfEvent({
826 cont.get_path_mut().push(path);
827 cont
828 }),
829 Subsystem::NetPrio(mut cont) => Subsystem::NetPrio({
830 cont.get_path_mut().push(path);
831 cont
832 }),
833 Subsystem::HugeTlb(mut cont) => Subsystem::HugeTlb({
834 cont.get_path_mut().push(path);
835 cont
836 }),
837 Subsystem::Rdma(mut cont) => Subsystem::Rdma({
838 cont.get_path_mut().push(path);
839 cont
840 }),
841 Subsystem::Systemd(mut cont) => Subsystem::Systemd({
842 cont.get_path_mut().push(path);
843 cont
844 }),
845 }
846 }
847
848 pub fn to_controller(&self) -> &dyn Controller {
849 match self {
850 Subsystem::Pid(cont) => cont,
851 Subsystem::Mem(cont) => cont,
852 Subsystem::CpuSet(cont) => cont,
853 Subsystem::CpuAcct(cont) => cont,
854 Subsystem::Cpu(cont) => cont,
855 Subsystem::Devices(cont) => cont,
856 Subsystem::Freezer(cont) => cont,
857 Subsystem::NetCls(cont) => cont,
858 Subsystem::BlkIo(cont) => cont,
859 Subsystem::PerfEvent(cont) => cont,
860 Subsystem::NetPrio(cont) => cont,
861 Subsystem::HugeTlb(cont) => cont,
862 Subsystem::Rdma(cont) => cont,
863 Subsystem::Systemd(cont) => cont,
864 }
865 }
866
867 pub fn controller_name(&self) -> String {
868 self.to_controller().control_type().to_string()
869 }
870}
871
872#[derive(Eq, PartialEq, Copy, Clone, Debug)]
874#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
875pub enum MaxValue {
876 Max,
878 Value(i64),
880}
881
882#[allow(clippy::derivable_impls)]
883impl Default for MaxValue {
884 fn default() -> Self {
885 MaxValue::Max
886 }
887}
888
889impl MaxValue {
890 #[allow(clippy::should_implement_trait, clippy::wrong_self_convention)]
891 fn to_i64(&self) -> i64 {
892 match self {
893 MaxValue::Max => -1,
894 MaxValue::Value(num) => *num,
895 }
896 }
897}
898
899impl fmt::Display for MaxValue {
900 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
901 match self {
902 MaxValue::Max => write!(f, "max"),
903 MaxValue::Value(num) => write!(f, "{}", num),
904 }
905 }
906}
907
908pub fn parse_max_value(s: &str) -> Result<MaxValue> {
909 if s.trim() == "max" {
910 return Ok(MaxValue::Max);
911 }
912 match s.trim().parse() {
913 Ok(val) => Ok(MaxValue::Value(val)),
914 Err(e) => Err(Error::with_cause(ParseError, e)),
915 }
916}
917
918pub fn flat_keyed_to_vec(mut file: File) -> Result<Vec<(String, i64)>> {
922 let mut content = String::new();
923 file.read_to_string(&mut content)
924 .map_err(|e| Error::with_cause(ReadFailed("FIXME: read_string_from".to_string()), e))?;
925
926 let mut v = Vec::new();
927 for line in content.lines() {
928 let parts: Vec<&str> = line.split(' ').collect();
929 if parts.len() == 2 {
930 if let Ok(i) = parts[1].parse::<i64>() {
931 v.push((parts[0].to_string(), i));
932 }
933 }
934 }
935 Ok(v)
936}
937
938pub fn flat_keyed_to_hashmap(mut file: File) -> Result<HashMap<String, i64>> {
942 let mut content = String::new();
943 file.read_to_string(&mut content)
944 .map_err(|e| Error::with_cause(ReadFailed("FIXME: read_string_from".to_string()), e))?;
945
946 let mut h = HashMap::new();
947 for line in content.lines() {
948 let parts: Vec<&str> = line.split(' ').collect();
949 if parts.len() == 2 {
950 if let Ok(i) = parts[1].parse::<i64>() {
951 h.insert(parts[0].to_string(), i);
952 }
953 }
954 }
955 Ok(h)
956}
957
958pub fn nested_keyed_to_hashmap(mut file: File) -> Result<HashMap<String, HashMap<String, i64>>> {
962 let mut content = String::new();
963 file.read_to_string(&mut content)
964 .map_err(|e| Error::with_cause(ReadFailed("FIXME: read_string_from".to_string()), e))?;
965
966 let mut h = HashMap::new();
967 for line in content.lines() {
968 let parts: Vec<&str> = line.split(' ').collect();
969 if parts.is_empty() {
970 continue;
971 }
972 let mut th = HashMap::new();
973 for item in parts[1..].iter() {
974 let fields: Vec<&str> = item.split('=').collect();
975 if fields.len() == 2 {
976 if let Ok(i) = fields[1].parse::<i64>() {
977 th.insert(fields[0].to_string(), i);
978 }
979 }
980 }
981 h.insert(parts[0].to_string(), th);
982 }
983
984 Ok(h)
985}
986
987fn read_from<T>(mut file: File) -> Result<T>
988where
989 T: FromStr,
990 <T as FromStr>::Err: 'static + Send + Sync + std::error::Error,
991{
992 let mut string = String::new();
993 match file.read_to_string(&mut string) {
994 Ok(_) => string
995 .trim()
996 .parse::<T>()
997 .map_err(|e| Error::with_cause(ParseError, e)),
998 Err(e) => Err(Error::with_cause(
999 ReadFailed("FIXME: can't get path in fn read_from".to_string()),
1000 e,
1001 )),
1002 }
1003}
1004
1005fn read_string_from(mut file: File) -> Result<String> {
1006 let mut string = String::new();
1007 match file.read_to_string(&mut string) {
1008 Ok(_) => Ok(string.trim().to_string()),
1009 Err(e) => Err(Error::with_cause(
1010 ReadFailed("FIXME: can't get path in fn read_string_from".to_string()),
1011 e,
1012 )),
1013 }
1014}
1015
1016fn read_u64_from(file: File) -> Result<u64> {
1018 read_from::<u64>(file)
1019}
1020
1021fn read_i64_from(file: File) -> Result<i64> {
1023 read_from::<i64>(file)
1024}