Skip to content

Commit d540eef

Browse files
committed
Create processing context out of CLI options
1 parent 9d24b73 commit d540eef

File tree

3 files changed

+154
-5
lines changed

3 files changed

+154
-5
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2024 uutils
3+
Copyright (c) 2025 Diomidis Spinellis
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

src/uu/sed/src/command.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// Definitions for the compiled code data structures
22
//
3-
// This file is part of the uutils sed package.
3+
// SPDX-License-Identifier: MIT
4+
// Copyright (c) 2025 Diomidis Spinellis
45
//
6+
// This file is part of the uutils sed package.
7+
// It is licensed under the MIT License.
58
// For the full copyright and license information, please view the LICENSE
69
// file that was distributed with this source code.
710

@@ -13,6 +16,29 @@ use std::collections::HashMap;
1316
use std::fs::File;
1417
use std::path::PathBuf; // For file descriptors and equivalent
1518

19+
// Compilation and processing context
20+
#[derive(Debug)]
21+
pub struct Context {
22+
// Input file/string and position
23+
pub input: String,
24+
pub line: usize,
25+
26+
// Command-line flags with corresponding names
27+
pub all_output_files: bool,
28+
pub debug: bool,
29+
pub regexp_extended: bool,
30+
pub follow_symlinks: bool,
31+
pub in_place: bool,
32+
pub in_place_suffix: Option<String>,
33+
pub length: usize,
34+
pub quiet: bool,
35+
pub posix: bool,
36+
pub separate: bool,
37+
pub sandbox: bool,
38+
pub unbuffered: bool,
39+
pub null_data: bool,
40+
}
41+
1642
// The specification of a script: through a string or a file
1743
#[derive(Debug, PartialEq)]
1844
pub enum ScriptValue {

src/uu/sed/src/sed.rs

Lines changed: 126 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
// This file is part of the uutils sed package.
1+
// Program entry point and CLI processing
2+
//
3+
// SPDX-License-Identifier: MIT
4+
// Copyright (c) 2025 Diomidis Spinellis
25
//
6+
// This file is part of the uutils sed package.
7+
// It is licensed under the MIT License.
38
// For the full copyright and license information, please view the LICENSE
49
// file that was distributed with this source code.
510

611
pub mod command;
712
pub mod compiler;
813
pub mod processor;
914

10-
use crate::command::ScriptValue;
15+
use crate::command::{Context, ScriptValue};
1116
use crate::compiler::compile;
1217
use crate::processor::process;
1318
use clap::{arg, Arg, ArgMatches, Command};
@@ -96,6 +101,8 @@ fn get_scripts_files(matches: &ArgMatches) -> UResult<(Vec<ScriptValue>, Vec<Pat
96101
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
97102
let matches = uu_app().try_get_matches_from(args)?;
98103
let (scripts, files) = get_scripts_files(&matches)?;
104+
let _context = build_context(&matches);
105+
99106
let executable = compile(scripts)?;
100107
process(executable, files)?;
101108
Ok(())
@@ -123,7 +130,8 @@ pub fn uu_app() -> Command {
123130
.short('E')
124131
.long("regexp-extended")
125132
.short_alias('r')
126-
.help("Use extended regular expressions."),
133+
.help("Use extended regular expressions.")
134+
.action(clap::ArgAction::SetTrue),
127135
arg!(-e --expression <SCRIPT> "Add script to executed commands.")
128136
.action(clap::ArgAction::Append),
129137
// Access with .get_many::<PathBuf>("file")
@@ -160,6 +168,38 @@ pub fn uu_app() -> Command {
160168
])
161169
}
162170

171+
// Parse CLI flag arguments and return a Context struct based on them
172+
fn build_context(matches: &ArgMatches) -> Context {
173+
Context {
174+
input: String::new(),
175+
line: 1,
176+
177+
// Flags
178+
all_output_files: matches.get_flag("all-output-files"),
179+
debug: matches.get_flag("debug"),
180+
regexp_extended: matches.get_flag("regexp-extended"),
181+
follow_symlinks: matches.get_flag("follow-symlinks"),
182+
in_place: matches.contains_id("in-place"),
183+
in_place_suffix: matches.get_one::<String>("in-place").and_then(|s| {
184+
if s.is_empty() {
185+
None
186+
} else {
187+
Some(s.clone())
188+
}
189+
}),
190+
length: matches
191+
.get_one::<u32>("length")
192+
.map(|v| *v as usize)
193+
.unwrap_or(70),
194+
quiet: matches.get_flag("quiet"),
195+
posix: matches.get_flag("posix"),
196+
separate: matches.get_flag("separate"),
197+
sandbox: matches.get_flag("sandbox"),
198+
unbuffered: matches.get_flag("unbuffered"),
199+
null_data: matches.get_flag("null-data"),
200+
}
201+
}
202+
163203
#[cfg(test)]
164204
mod tests {
165205
use super::*; // Allows access to private functions/items in this module
@@ -257,4 +297,87 @@ mod tests {
257297
);
258298
assert_eq!(files, vec![PathBuf::from("-")]); // Stdin should be used
259299
}
300+
301+
fn test_matches(args: &[&str]) -> ArgMatches {
302+
uu_app().get_matches_from(["sed"].into_iter().chain(args.iter().copied()))
303+
}
304+
305+
#[test]
306+
fn test_defaults() {
307+
let matches = test_matches(&[]);
308+
let ctx = build_context(&matches);
309+
310+
assert_eq!(ctx.input.len(), 0);
311+
assert_eq!(ctx.line, 1);
312+
assert!(!ctx.all_output_files);
313+
assert!(!ctx.debug);
314+
assert!(!ctx.regexp_extended);
315+
assert!(!ctx.follow_symlinks);
316+
assert!(!ctx.in_place);
317+
assert_eq!(ctx.in_place_suffix, None);
318+
assert_eq!(ctx.length, 70);
319+
assert!(!ctx.quiet);
320+
assert!(!ctx.posix);
321+
assert!(!ctx.separate);
322+
assert!(!ctx.sandbox);
323+
assert!(!ctx.unbuffered);
324+
assert!(!ctx.null_data);
325+
}
326+
327+
#[test]
328+
fn test_all_flags() {
329+
let matches = test_matches(&[
330+
"--all-output-files",
331+
"--debug",
332+
"-E",
333+
"--follow-symlinks",
334+
"-i",
335+
"-l",
336+
"80",
337+
"-n",
338+
"--posix",
339+
"-s",
340+
"--sandbox",
341+
"-u",
342+
"-z",
343+
]);
344+
345+
let ctx = build_context(&matches);
346+
347+
assert_eq!(ctx.input.len(), 0);
348+
assert!(ctx.all_output_files);
349+
assert!(ctx.debug);
350+
assert!(ctx.regexp_extended);
351+
assert!(ctx.follow_symlinks);
352+
assert!(ctx.in_place);
353+
assert!(ctx.in_place_suffix.is_none());
354+
assert_eq!(ctx.length, 80);
355+
assert!(ctx.quiet);
356+
assert!(ctx.posix);
357+
assert!(ctx.separate);
358+
assert!(ctx.sandbox);
359+
assert!(ctx.unbuffered);
360+
assert!(ctx.null_data);
361+
}
362+
363+
#[test]
364+
fn test_in_place_with_suffix() {
365+
let matches = test_matches(&["-i", ".bak"]);
366+
let ctx = build_context(&matches);
367+
368+
assert!(ctx.in_place);
369+
assert_eq!(ctx.in_place_suffix, Some(".bak".to_string()));
370+
}
371+
372+
#[test]
373+
fn test_length_default_and_custom() {
374+
let matches_default = test_matches(&[]);
375+
let matches_custom = test_matches(&["-l", "120"]);
376+
377+
let ctx_default = build_context(&matches_default);
378+
let ctx_custom = build_context(&matches_custom);
379+
380+
assert_eq!(ctx_default.length, 70);
381+
assert_eq!(ctx_custom.length, 120);
382+
}
260383
}

0 commit comments

Comments
 (0)