lbf/io/
mod.rs

1use std::fs;
2use std::fs::File;
3use std::io::{BufReader, BufWriter};
4use std::path::Path;
5
6use crate::EPOCH;
7use log::{Level, LevelFilter, info, log};
8use serde::Serialize;
9use svg::Document;
10
11use anyhow::{Context, Result};
12use jagua_rs::probs::bpp::io::ext_repr::ExtBPInstance;
13use jagua_rs::probs::spp::io::ext_repr::ExtSPInstance;
14
15pub mod cli;
16pub mod output;
17
18pub fn read_spp_instance(path: &Path) -> Result<ExtSPInstance> {
19    let file = File::open(path).context("could not open instance file")?;
20    serde_json::from_reader(BufReader::new(file))
21        .context("not a valid strip packing instance (ExtSPInstance)")
22}
23
24pub fn read_bpp_instance(path: &Path) -> Result<ExtBPInstance> {
25    let file = File::open(path).context("could not open instance file")?;
26    serde_json::from_reader(BufReader::new(file))
27        .context("not a valid bin packing instance (ExtBPInstance)")
28}
29
30pub fn write_json(json: &impl Serialize, path: &Path) -> Result<()> {
31    let file = File::create(path)?;
32    let writer = BufWriter::new(file);
33
34    serde_json::to_writer_pretty(writer, &json)?;
35
36    info!(
37        "Solution JSON written to file://{}",
38        fs::canonicalize(path)?.to_str().unwrap()
39    );
40    Ok(())
41}
42
43pub fn write_svg(document: &Document, path: &Path) -> Result<()> {
44    svg::save(path, document)?;
45    info!(
46        "Solution SVG written to file://{}",
47        fs::canonicalize(path)
48            .expect("could not canonicalize path")
49            .to_str()
50            .unwrap()
51    );
52    Ok(())
53}
54
55pub fn init_logger(level_filter: LevelFilter) -> Result<()> {
56    fern::Dispatch::new()
57        // Perform allocation-free log formatting
58        .format(|out, message, record| {
59            let handle = std::thread::current();
60            let thread_name = handle.name().unwrap_or("-");
61
62            let duration = EPOCH.elapsed();
63            let sec = duration.as_secs() % 60;
64            let min = (duration.as_secs() / 60) % 60;
65            let hours = (duration.as_secs() / 60) / 60;
66
67            let prefix = format!(
68                "[{}] [{:0>2}:{:0>2}:{:0>2}] <{}>",
69                record.level(),
70                hours,
71                min,
72                sec,
73                thread_name,
74            );
75
76            out.finish(format_args!("{prefix:<27}{message}"))
77        })
78        // Add blanket level filter
79        .level(level_filter)
80        .chain(std::io::stdout())
81        .apply()?;
82    log!(Level::Info, "Epoch: {}", jiff::Timestamp::now());
83    Ok(())
84}