Skip to content

Commit 63aec4b

Browse files
authored
Logger (#76)
* logging: make it configurable via new -v argument * logging: remove the timestamps in formatting * make use of the logger some more and silence the "hops" (print!(".")). * allow to log into a file or stderr based on new -l argument * move logging setup out of the main file to avoid clutter * move definition of cli parser out ot main to avoid clutter * respond to codepilot suggestions and cargo clippy
1 parent 4a84499 commit 63aec4b

File tree

5 files changed

+134
-81
lines changed

5 files changed

+134
-81
lines changed

src/cli.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//! This module defines the command line interface (CLI) for the application.
2+
3+
use clap::{Parser, ValueEnum};
4+
use std::path::PathBuf;
5+
use crate::solver;
6+
use crate::nfa;
7+
8+
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
9+
pub enum OutputFormat {
10+
Plain,
11+
Tex,
12+
Csv,
13+
}
14+
15+
#[derive(Parser, Debug)]
16+
#[command(version, about, long_about = None)]
17+
pub struct Args {
18+
#[arg(value_name = "AUTOMATON_FILE", help = "Path to the input")]
19+
pub filename: String,
20+
21+
#[arg(
22+
short = 'f',
23+
long = "from",
24+
value_enum,
25+
default_value = "tikz",
26+
help = "The input format"
27+
)]
28+
pub input_format: nfa::InputFormat,
29+
30+
#[arg(
31+
short = 'v',
32+
long = "verbose",
33+
action = clap::ArgAction::Count,
34+
help = "Increase verbosity level"
35+
)]
36+
pub verbosity: u8,
37+
38+
#[arg(
39+
long,
40+
short = 'l',
41+
value_name = "LOG_FILE",
42+
help = "Optional path to the log file. Defaults to stdout if not specified."
43+
)]
44+
pub log_output: Option<PathBuf>,
45+
46+
#[arg(
47+
value_enum,
48+
short = 't',
49+
long = "to",
50+
default_value = "plain",
51+
help = "The output format"
52+
)]
53+
pub output_format: OutputFormat,
54+
55+
#[arg(
56+
short = 'o',
57+
long = "output",
58+
value_name = "OUTPUT_FILE",
59+
help = "Where to write the strategy; defaults to stdout."
60+
)]
61+
pub output_path: Option<PathBuf>,
62+
63+
#[arg(
64+
short,
65+
long,
66+
value_enum,
67+
default_value = "input",
68+
help = "The state reordering type."
69+
)]
70+
pub state_ordering: nfa::StateOrdering,
71+
72+
#[arg(
73+
long,
74+
value_enum,
75+
default_value = "strategy",
76+
help = "Solver output specification."
77+
)]
78+
pub solver_output: solver::SolverOutput,
79+
}

src/logging.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
2+
//! This module provides functionality for setting up logging
3+
4+
use env_logger::Builder;
5+
use log::LevelFilter;
6+
use std::fs::File;
7+
use std::io::Write;
8+
use std::path::PathBuf;
9+
10+
/// Sets up the logger based on verbosity and optional log file path.
11+
pub fn setup_logger(verbosity: u8, log_output: Option<PathBuf>) {
12+
let mut builder = Builder::from_default_env();
13+
builder.format_timestamp(None);
14+
15+
builder.filter_level(match verbosity {
16+
0 => LevelFilter::Warn,
17+
1 => LevelFilter::Info,
18+
2 => LevelFilter::Debug,
19+
_ => LevelFilter::Trace,
20+
});
21+
22+
if let Some(log_path) = log_output {
23+
match File::create(&log_path) {
24+
Ok(file) => {
25+
let writer = Box::new(file) as Box<dyn Write + Send>;
26+
builder.target(env_logger::Target::Pipe(writer));
27+
}
28+
Err(_) => {
29+
eprintln!("Could not create log file at {}. Defaulting to stderr.", log_path.display());
30+
builder.target(env_logger::Target::Stderr);
31+
}
32+
}
33+
} else {
34+
builder.target(env_logger::Target::Stderr);
35+
}
36+
37+
builder.init();
38+
}

src/main.rs

Lines changed: 12 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
use clap::{Parser, ValueEnum};
1+
use clap::Parser;
22
use std::fs::File;
33
use std::io;
44
use std::io::Write;
5-
use std::path::PathBuf;
65
mod coef;
76
mod downset;
87
mod flow;
@@ -15,85 +14,24 @@ mod semigroup;
1514
mod solution;
1615
mod solver;
1716
mod strategy;
18-
use log::LevelFilter;
17+
use log::info;
18+
mod logging;
19+
mod cli;
1920

20-
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
21-
enum OutputFormat {
22-
Plain,
23-
Tex,
24-
Csv,
25-
}
26-
27-
#[derive(clap::Parser, Debug)]
28-
#[command(version, about, long_about = None)]
29-
struct Args {
30-
#[arg(value_name = "AUTOMATON_FILE", help = "path to the input")]
31-
filename: String,
32-
33-
#[arg(
34-
short = 'f',
35-
long = "from",
36-
value_enum,
37-
default_value = "tikz",
38-
help = "The input format"
39-
)]
40-
input_format: nfa::InputFormat,
41-
42-
#[arg(
43-
value_enum,
44-
short = 't',
45-
long = "to",
46-
default_value = "plain",
47-
help = "The output format"
48-
)]
49-
output_format: OutputFormat,
50-
51-
/// path to write the strategy
52-
#[arg(
53-
short = 'o',
54-
long = "output",
55-
value_name = "OUTPUT_FILE",
56-
help = "where to write the strategy; defaults to stdout."
57-
)]
58-
output_path: Option<PathBuf>,
59-
60-
#[arg(
61-
short,
62-
long,
63-
value_enum,
64-
default_value = "input",
65-
help = format!("The state reordering type: preserves input order, sorts alphabetically or topologically.")
66-
)]
67-
state_ordering: nfa::StateOrdering,
68-
69-
#[arg(
70-
long,
71-
value_enum,
72-
default_value = "strategy",
73-
help = format!("The solver output. Either yes/no and a winning strategy (the faster). Or the full maximal winning strategy.")
74-
)]
75-
solver_output: solver::SolverOutput,
76-
}
7721

7822
fn main() {
79-
#[cfg(debug_assertions)]
80-
env_logger::Builder::new()
81-
.filter_level(LevelFilter::Debug)
82-
.init();
83-
84-
#[cfg(not(debug_assertions))]
85-
env_logger::Builder::new()
86-
.filter_level(LevelFilter::Info)
87-
.init();
8823

8924
// parse CLI arguments
90-
let args = Args::parse();
25+
let args = cli::Args::parse();
26+
27+
// set up logging
28+
logging::setup_logger(args.verbosity, args.log_output);
9129

9230
// parse the input file
9331
let nfa = nfa::Nfa::load_from_file(&args.filename, &args.input_format, &args.state_ordering);
9432

9533
// print the input automaton
96-
println!("{}", nfa);
34+
info!("{}", nfa);
9735

9836
// compute the solution
9937
let solution = solver::solve(&nfa, &args.solver_output);
@@ -135,20 +73,20 @@ fn main() {
13573

13674
// prepare output string
13775
let output = match args.output_format {
138-
OutputFormat::Tex => {
76+
cli::OutputFormat::Tex => {
13977
let is_tikz = args.input_format == nfa::InputFormat::Tikz;
14078
let latex_content =
14179
solution.as_latex(if is_tikz { Some(&args.filename) } else { None });
14280
latex_content.to_string()
14381
}
144-
OutputFormat::Plain => {
82+
cli::OutputFormat::Plain => {
14583
format!(
14684
"States: {}\n {}",
14785
nfa.states_str(),
14886
solution.winning_strategy
14987
)
15088
}
151-
OutputFormat::Csv => {
89+
cli::OutputFormat::Csv => {
15290
format!(
15391
"Σ, {}\n{}\n",
15492
nfa.states().join(","),

src/semigroup.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use rayon::prelude::*;
88
use std::collections::HashSet; // for distinct method
99
use std::collections::VecDeque;
1010
use std::fmt;
11-
use std::io::{self, Write};
1211

1312
pub struct FlowSemigroup {
1413
//invariant: all flows have the same dimension
@@ -194,8 +193,8 @@ impl FlowSemigroup {
194193
let mut changed = false;
195194
while !to_process_mult.is_empty() {
196195
let flow = to_process_mult.pop_front().unwrap();
197-
print!(".");
198-
io::stdout().flush().unwrap();
196+
//print!(".");
197+
//io::stdout().flush().unwrap();
199198
debug!("\nClose by product processing flow\n{}\n", flow);
200199
/*if Self::is_covered(&flow, &processed) {
201200
//debug!("Skipped inqueue\n{}", flow);
@@ -250,7 +249,7 @@ impl FlowSemigroup {
250249
while !to_process_iter.is_empty() {
251250
let flow = to_process_iter.pop_front().unwrap();
252251
debug_assert!(flow.is_idempotent());
253-
print!(".");
252+
//print!(".");
254253
debug!("\nClose by product processing flow\n{}\n", flow);
255254
let iteration = flow.iteration();
256255
if !Self::is_covered(&iteration, &self.flows) {

src/strategy.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::downset::DownSet;
33
use crate::graph::Graph;
44
use crate::ideal::Ideal;
55
use crate::nfa;
6-
use std::io::{self, Write};
76

87
use std::collections::HashMap;
98
use std::fmt;
@@ -38,8 +37,8 @@ impl Strategy {
3837
) -> bool {
3938
let mut result = false;
4039
for (a, downset) in self.0.iter_mut() {
41-
print!(".");
42-
io::stdout().flush().unwrap();
40+
// print!(".");
41+
//io::stdout().flush().unwrap();
4342
let edges = edges_per_letter.get(a).unwrap();
4443
let safe_pre_image = safe.safe_pre_image(edges, maximal_finite_value);
4544
result |= downset.restrict_to(&safe_pre_image);

0 commit comments

Comments
 (0)