Skip to content

Commit

Permalink
Merge pull request #37 from DrLuke/address-lifetime
Browse files Browse the repository at this point in the history
Store owned address in OscAddress
  • Loading branch information
klingtnet authored Aug 23, 2022
2 parents 664d103 + 11e72ac commit f847042
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 54 deletions.
16 changes: 8 additions & 8 deletions src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ use nom::{IResult, Parser};
///
/// A valid OSC address begins with a `/` and contains at least a method name, e.g. `/tempo`.
/// A plain address must not include any of the following characters `#*,/?[]{}`, since they're reserved for OSC address patterns.
pub struct OscAddress<'a>(&'a str);
pub struct OscAddress(String);

impl<'a> OscAddress<'a> {
pub fn new(address: &'a str) -> Result<Self, OscError> {
match verify_address(address) {
impl OscAddress {
pub fn new(address: String) -> Result<Self, OscError> {
match verify_address(&address) {
Ok(_) => Ok(OscAddress(address)),
Err(e) => Err(e),
}
Expand Down Expand Up @@ -81,16 +81,16 @@ impl Matcher {
/// use rosc::address::{Matcher, OscAddress};
///
/// let matcher = Matcher::new("/oscillator/[0-9]/{frequency,phase}").unwrap();
/// assert!(matcher.match_address(&OscAddress::new("/oscillator/1/frequency").unwrap()));
/// assert!(matcher.match_address(&OscAddress::new("/oscillator/8/phase").unwrap()));
/// assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/4/detune").unwrap()), false);
/// assert!(matcher.match_address(&OscAddress::new(String::from("/oscillator/1/frequency")).unwrap()));
/// assert!(matcher.match_address(&OscAddress::new(String::from("/oscillator/8/phase")).unwrap()));
/// assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/4/detune")).unwrap()), false);
/// ```
pub fn match_address(&self, address: &OscAddress) -> bool {
// Trivial case
if address.0 == self.pattern {
return true;
}
let mut remainder: &str = address.0;
let mut remainder: &str = address.0.as_str();
// Match the the address component by component
for (index, part) in self.pattern_parts.as_slice().iter().enumerate() {
let result = match part {
Expand Down
92 changes: 46 additions & 46 deletions tests/address_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,128 +11,128 @@ fn test_matcher() {

// Regular address using only alphanumeric parts
matcher = Matcher::new("/oscillator/1/frequency").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/1/frequency").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/1/phase").expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/1/frequencyfoo").expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new("/prefix/oscillator/1/frequency").expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/1/frequency")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/1/phase")).expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/1/frequencyfoo")).expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/prefix/oscillator/1/frequency")).expect("Valid address pattern")), false);

// Choice
matcher = Matcher::new("/foo{bar,baz}").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/foobar").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/foobaz").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/foobar")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/foobaz")).expect("Valid address pattern")), true);

matcher = Matcher::new("/foo{bar,baz,tron}").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/footron").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/footron")).expect("Valid address pattern")), true);

// Character class
// Character classes are sets or ranges of characters to match.
// e.g. [a-z] will match any lower case alphabetic character. [abcd] will match the characters abcd.
// They can be negated with '!', e.g. [!0-9] will match all characters except 0-9
// Basic example
matcher = Matcher::new("/oscillator/[0-9]").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/0").expect("Valid address pattern")), true); // Beginning of range included
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/6").expect("Valid address pattern")), true); // Middle of range
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/9").expect("Valid address pattern")), true); // Last member of range included
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/0")).expect("Valid address pattern")), true); // Beginning of range included
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/6")).expect("Valid address pattern")), true); // Middle of range
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/9")).expect("Valid address pattern")), true); // Last member of range included

// Inverted order should fail
Matcher::new("/oscillator/[9-0]").expect_err("Inverted range accepted");

// Multiple ranges
matcher = Matcher::new("/oscillator/[a-zA-Z0-9]").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/0").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/a").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/A").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/0")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/a")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/A")).expect("Valid address pattern")), true);

// Negated range
matcher = Matcher::new("/oscillator/[!0-9]").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/1").expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/a").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/1")).expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/a")).expect("Valid address pattern")), true);

// Extra exclamation points must be entirely ignored
matcher = Matcher::new("/oscillator/[!0-9!a-z!]").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/A").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/A")).expect("Valid address pattern")), true);

// Trailing dash has no special meaning
matcher = Matcher::new("/oscillator/[abcd-]").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/a").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/-").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/a")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/-")).expect("Valid address pattern")), true);

// Single wildcard
// A single wildcard '?' matches excatly one alphanumeric character
matcher = Matcher::new("/oscillator/?/frequency").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/1/frequency").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/F/frequency").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/10/frequency").expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/1/frequency")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/F/frequency")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/10/frequency")).expect("Valid address pattern")), false);

// Test if two consecutive wildcards match
matcher = Matcher::new("/oscillator/??/frequency").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/10/frequency").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/1/frequency").expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/10/frequency")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/1/frequency")).expect("Valid address pattern")), false);

// Test if it works if it is surrounded by non-wildcards
matcher = Matcher::new("/oscillator/prefixed?postfixed/frequency").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/prefixed1postfixed/frequency").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/prefixedpostfixed/frequency").expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/prefixed1postfixed/frequency")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/prefixedpostfixed/frequency")).expect("Valid address pattern")), false);

// Wildcard
// Wildcards '*' match zero or more alphanumeric characters. The implementation is greedy,
// meaning it will match the longest possible sequence
matcher = Matcher::new("/oscillator/*/frequency").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/anything123/frequency").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~/frequency").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/anything123/frequency")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~/frequency")).expect("Valid address pattern")), true);
// Test that wildcard doesn't cross part boundary
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/extra/part/frequency").expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/extra/part/frequency")).expect("Valid address pattern")), false);

// Test greediness
matcher = Matcher::new("/oscillator/*bar/frequency").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/foobar/frequency").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/foobarbar/frequency").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/foobar/frequency")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/foobarbar/frequency")).expect("Valid address pattern")), true);

// Minimum length of 2
matcher = Matcher::new("/oscillator/*??/frequency").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/foobar/frequency").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/f/frequency").expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/foobar/frequency")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/f/frequency")).expect("Valid address pattern")), false);

// Minimum length of 2 and another component follows
matcher = Matcher::new("/oscillator/*??baz/frequency").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/foobarbaz/frequency").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/fbaz/frequency").expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/foobarbaz/frequency")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/fbaz/frequency")).expect("Valid address pattern")), false);

// Mix with character class
matcher = Matcher::new("/oscillator/*[a-d]/frequency").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/a/frequency").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/fooa/frequency").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/foox/frequency").expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/a/frequency")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/fooa/frequency")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/foox/frequency")).expect("Valid address pattern")), false);

// Mix with choice
matcher = Matcher::new("/oscillator/*{bar,baz}/frequency").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/foobar/frequency").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/baz/frequency").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/something/frequency").expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/foobar/frequency")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/baz/frequency")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/something/frequency")).expect("Valid address pattern")), false);

// Wildcard as last part
matcher = Matcher::new("/oscillator/*").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/foobar").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/foobar/frequency").expect("Valid address pattern")), false);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/foobar")).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/foobar/frequency")).expect("Valid address pattern")), false);

// Wildcard with more components in part but it's the last part
matcher = Matcher::new("/oscillator/*bar").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/oscillator/foobar").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/oscillator/foobar")).expect("Valid address pattern")), true);

// Check for allowed literal characters
matcher = Matcher::new("/!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~")).expect("Valid address pattern")), true);

// Check that single wildcard matches all legal characters
matcher = Matcher::new("/?").expect("Should be valid");
let legal = "!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~";
for c in legal.chars() {
assert_eq!(matcher.match_address(&OscAddress::new(format!("/{}", c).as_str()).expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(format!("/{}", c)).expect("Valid address pattern")), true);
}

// Make sure the character class deduplicator is triggered for code coverage
matcher = Matcher::new("/[a-za-za-z]").expect("Should be valid");
assert_eq!(matcher.match_address(&OscAddress::new("/a").expect("Valid address pattern")), true);
assert_eq!(matcher.match_address(&OscAddress::new(String::from("/a")).expect("Valid address pattern")), true);
}

#[cfg(feature = "std")]
Expand Down

0 comments on commit f847042

Please sign in to comment.