pub struct ControlLine { /* private fields */ }Expand description
A single line from a control protocol response with parsing utilities.
ControlLine provides methods for parsing space-delimited entries from
control protocol response lines. It maintains internal state to track
the unparsed remainder, allowing sequential extraction of values.
§Parsing Model
The line is treated as a sequence of space-separated entries that can be “popped” from left to right. Each pop operation:
- Extracts the next entry (optionally handling quotes and escapes)
- Updates the internal remainder to exclude the extracted entry
- Returns the extracted value
§Entry Types
- Simple values: Space-separated tokens (e.g.,
PROTOCOLINFO 1) - Quoted values: Values enclosed in double quotes (e.g.,
"hello world") - Key-value mappings:
KEY=VALUEpairs (e.g.,Tor="0.4.7.8") - Escaped values: Values with backslash escapes (e.g.,
"path\\to\\file")
§Thread Safety
ControlLine uses internal synchronization (Mutex) for its mutable
parsing state, making it safe to share across threads. However, concurrent
pops from multiple threads will produce unpredictable results.
§Example
use stem_rs::response::ControlLine;
let mut line = ControlLine::new("AUTH METHODS=COOKIE,PASSWORD VERSION=\"1.0\"");
// Pop simple value
assert_eq!(line.pop(false, false).unwrap(), "AUTH");
// Pop key-value mapping
let (key, value) = line.pop_mapping(false, false).unwrap();
assert_eq!(key, "METHODS");
assert_eq!(value, "COOKIE,PASSWORD");
// Pop quoted key-value mapping
let (key, value) = line.pop_mapping(true, false).unwrap();
assert_eq!(key, "VERSION");
assert_eq!(value, "1.0");
assert!(line.is_empty());Implementations§
Source§impl ControlLine
impl ControlLine
Sourcepub fn new(content: &str) -> Self
pub fn new(content: &str) -> Self
Creates a new ControlLine from the given content.
The entire content is initially available as the remainder for parsing.
§Arguments
content- The line content to parse
§Example
use stem_rs::response::ControlLine;
let line = ControlLine::new("KEY=value more data");
assert_eq!(line.remainder(), "KEY=value more data");Sourcepub fn remainder(&self) -> String
pub fn remainder(&self) -> String
Returns the unparsed remainder of the line.
This is the portion of the line that hasn’t been consumed by
pop or pop_mapping calls.
§Returns
The remaining unparsed content as a string.
§Example
use stem_rs::response::ControlLine;
let mut line = ControlLine::new("first second third");
assert_eq!(line.remainder(), "first second third");
line.pop(false, false).unwrap();
assert_eq!(line.remainder(), "second third");Sourcepub fn is_next_quoted(&self, escaped: bool) -> bool
pub fn is_next_quoted(&self, escaped: bool) -> bool
Checks if the next entry is a quoted value.
A quoted value starts with a double quote (") and has a matching
closing quote.
§Arguments
escaped- Iftrue, handles backslash-escaped quotes within the value
§Returns
true if the next entry can be parsed as a quoted value, false otherwise.
§Example
use stem_rs::response::ControlLine;
let line = ControlLine::new("\"quoted value\" unquoted");
assert!(line.is_next_quoted(false));
let line = ControlLine::new("unquoted \"quoted\"");
assert!(!line.is_next_quoted(false));Sourcepub fn is_next_mapping(
&self,
key: Option<&str>,
quoted: bool,
escaped: bool,
) -> bool
pub fn is_next_mapping( &self, key: Option<&str>, quoted: bool, escaped: bool, ) -> bool
Checks if the next entry is a KEY=VALUE mapping.
§Arguments
key- IfSome, checks that the key matches this specific valuequoted- Iftrue, checks that the value is quotedescaped- Iftrue, handles backslash escapes in quoted values
§Returns
true if the next entry is a valid KEY=VALUE mapping matching the criteria.
§Example
use stem_rs::response::ControlLine;
let line = ControlLine::new("KEY=value OTHER=stuff");
// Check for any mapping
assert!(line.is_next_mapping(None, false, false));
// Check for specific key
assert!(line.is_next_mapping(Some("KEY"), false, false));
assert!(!line.is_next_mapping(Some("OTHER"), false, false));
// Check for quoted value
let line = ControlLine::new("KEY=\"quoted\"");
assert!(line.is_next_mapping(Some("KEY"), true, false));Sourcepub fn peek_key(&self) -> Option<String>
pub fn peek_key(&self) -> Option<String>
Returns the key of the next entry without consuming it.
If the next entry is a KEY=VALUE mapping, returns the key portion.
Otherwise returns None.
§Returns
Some(key) if the next entry is a mapping, None otherwise.
§Example
use stem_rs::response::ControlLine;
let line = ControlLine::new("MYKEY=myvalue");
assert_eq!(line.peek_key(), Some("MYKEY".to_string()));
let line = ControlLine::new("no_equals_here");
assert!(line.peek_key().is_none());Sourcepub fn pop(&mut self, quoted: bool, escaped: bool) -> Result<String, Error>
pub fn pop(&mut self, quoted: bool, escaped: bool) -> Result<String, Error>
Removes and returns the next space-separated entry.
This method extracts the next entry from the remainder, handling optional quoting and escape sequences.
§Arguments
quoted- Iftrue, parses the entry as a quoted value (removes quotes)escaped- Iftrue, processes backslash escape sequences
§Escape Sequences
When escaped is true, the following sequences are processed:
\\n→ newline\\r→ carriage return\\t→ tab\\\\→ backslash\\"→ double quote
§Errors
Returns Error::Protocol if:
- No remaining content to parse
quotedistruebut the next entry isn’t quoted
§Example
use stem_rs::response::ControlLine;
let mut line = ControlLine::new("\"We're all mad here.\" says the cat.");
// Pop quoted value
assert_eq!(line.pop(true, false).unwrap(), "We're all mad here.");
// Pop unquoted value
assert_eq!(line.pop(false, false).unwrap(), "says");
// Check remainder
assert_eq!(line.remainder(), "the cat.");Sourcepub fn pop_mapping(
&mut self,
quoted: bool,
escaped: bool,
) -> Result<(String, String), Error>
pub fn pop_mapping( &mut self, quoted: bool, escaped: bool, ) -> Result<(String, String), Error>
Removes and returns the next entry as a KEY=VALUE mapping.
Parses the next entry expecting a KEY=VALUE format and returns
both the key and value as separate strings.
§Arguments
quoted- Iftrue, the value is expected to be quotedescaped- Iftrue, processes backslash escape sequences in the value
§Errors
Returns Error::Protocol if:
- No remaining content to parse
- The next entry isn’t a KEY=VALUE mapping
quotedistruebut the value isn’t quoted
§Example
use stem_rs::response::ControlLine;
let mut line = ControlLine::new("Tor=\"0.4.7.8\" other=value");
// Pop quoted mapping
let (key, value) = line.pop_mapping(true, false).unwrap();
assert_eq!(key, "Tor");
assert_eq!(value, "0.4.7.8");
// Pop unquoted mapping
let (key, value) = line.pop_mapping(false, false).unwrap();
assert_eq!(key, "other");
assert_eq!(value, "value");Sourcepub fn pop_mapping_bytes(
&mut self,
quoted: bool,
escaped: bool,
) -> Result<(String, Vec<u8>), Error>
pub fn pop_mapping_bytes( &mut self, quoted: bool, escaped: bool, ) -> Result<(String, Vec<u8>), Error>
Removes and returns the next entry as a KEY=VALUE mapping with raw bytes.
Similar to pop_mapping, but returns the value as
raw bytes instead of a string. Useful when the value may contain binary
data or when exact byte preservation is required.
§Arguments
quoted- Iftrue, the value is expected to be quotedescaped- Iftrue, processes backslash escape sequences in the value
§Errors
Returns Error::Protocol if:
- No remaining content to parse
- The next entry isn’t a KEY=VALUE mapping
quotedistruebut the value isn’t quoted
§Example
use stem_rs::response::ControlLine;
let mut line = ControlLine::new("DATA=binary_content");
let (key, value_bytes) = line.pop_mapping_bytes(false, false).unwrap();
assert_eq!(key, "DATA");
assert_eq!(value_bytes, b"binary_content");