Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion module/move/assistant/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ serde_with = "3.11.0"
error_tools = "0.17.0"
derive_tools = { version = "0.32.0", features = ["full"] }
regex = { version = "1.10.3" }
itertools = "0.13.0"
serde_yaml = "0.9"

[dev-dependencies]
Expand Down
1 change: 1 addition & 0 deletions module/move/assistant/src/agents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ crate::mod_interface!
layer context;
layer scenario_raw;
layer scenario_raw_processors;
layer scenario_processed;

}
72 changes: 14 additions & 58 deletions module/move/assistant/src/agents/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,62 +9,14 @@ mod private
use std::collections::HashMap;

use crate::*;
use agents::path::Path;

/// Simplistic in-memory "filesystem". Represents the root of the filesystem.
///
/// `T` is the type of terminal object.
#[ derive( Debug, Default ) ]
pub struct Context< T >
{
root : ContextDir< T >,
}

impl< T > Context< T >
use agents::path::
{
/// Create an empty `Context`.
pub fn new() -> Self
{
Self
{
root : ContextDir::new()
}
}

/// Add new entry to the directory.
///
/// Returns `true` if entry was successfully added.
/// Returns `false` if there is already and entry with such name.
/// Old entry will not be overriden.
pub fn add( &mut self, name : impl Into< String >, entry : ContextEntry< T > ) -> bool
{
self.root.add( name, entry )
}

/// Get an entry by its name. Returns `None` is there is no such entry.
///
/// `name` must be a valid path item. Refer to `path::PATH_ITEM_REGEX_STR` for syntax.
///
/// This method is useful for quickly getting an entry only by its name.
/// For complex paths, where your object is located in several consecutives directories,
/// you can use `Path` type and use method `Context::get_by_path`.
pub fn get( &self, name : impl AsRef< str > ) -> Option< &ContextEntry< T > >
{
self.root.get( name )
}

/// Get an entry by its path. Returns `None` is there is no such entry.
///
/// This function can accept absolute `Path`s as `Context` represents the root of the
/// filesystem.
pub fn get_by_path( &self, path : &Path ) -> Option< &ContextEntry< T > >
{
self.root.get_by_path( &path.remove_absolute() )
}
}
Path,
PATH_SEPARATOR,
};

/// Represents a directory in `Context` with other directories and
/// terminal objects.
/// Represents a directory in a simplistic in-memory "filesystem"
/// with other directories and terminal objects.
///
/// `T` is the type of terminal object.
#[ derive( Debug, PartialEq, Clone, Default ) ]
Expand Down Expand Up @@ -119,14 +71,19 @@ mod private

/// Get an entry by its path. Returns `None` is there is no such entry.
///
/// This function does not accept absolute `Path`, as `ContextDir` does not know
/// whether it is root or not. For absolute `Path`s use `Context::get_by_path`.
/// This function accepts both relative and absolute paths and it will
/// treat itself as the root.
pub fn get_by_path( &self, path : &Path ) -> Option< &ContextEntry< T > >
{
let mut cur : Option< &ContextEntry< T > > = None;

for component in path.components()
{
if component == PATH_SEPARATOR
{
continue;
}

match cur
{
None =>
Expand Down Expand Up @@ -161,7 +118,7 @@ mod private
}
}

/// Entry in `Context`: either a directory or a terminal object `T`.
/// Entry in a simplistic in-memory "filesystem": either a directory or a terminal object `T`.
///
/// Notice, this struct does not store the name of the entry.
#[ derive( Debug, PartialEq, Clone ) ]
Expand All @@ -187,7 +144,6 @@ crate::mod_interface!
{
own use
{
Context,
ContextDir,
ContextEntry,
};
Expand Down
160 changes: 46 additions & 114 deletions module/move/assistant/src/agents/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ mod private
sync::LazyLock,
};

use itertools::Itertools;
use serde::
{
Serialize,
Deserialize,
};

use regex::Regex;

/// Path separator string.
Expand All @@ -24,20 +29,6 @@ mod private
/// If you want to match against this expression, use `PATH_ITEM_REGEX`.
pub const PATH_ITEM_REGEX_STR : &str = r"[a-zA-Z0-9_ -]+";

/// Regular expression for `Path` items. You can match whole `&str` with this type.
///
/// To match whole `Path` in strings, use `PATH_REGEX`.
pub static PATH_ITEM_REGEX : LazyLock< Regex > = LazyLock::new( ||
{
let regex = format!
(
r"^{}$",
PATH_ITEM_REGEX_STR
);

Regex::new( &regex ).unwrap()
});

/// Regular expression for `Path`. You can match whole `&str` with this type.
pub static PATH_REGEX : LazyLock< Regex > = LazyLock::new( ||
{
Expand All @@ -56,7 +47,7 @@ mod private
///
/// Paths resemble filesystem path, path separator is `::`.
/// Absolute path starts with `::`.
#[ derive( Debug, Clone, Eq, PartialEq, Hash ) ]
#[ derive( Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize ) ]
pub struct Path( String );

impl Path
Expand All @@ -67,6 +58,34 @@ mod private
#[ inline ]
pub fn parent( &self ) -> Option< Path >
{
/// Find parent of a `Path`.
///
/// This method uses `&str` as an argument instead of `Path`
/// in order to be more general and handle trailing `::` case.
fn find_parent( s : &str ) -> Option< &str >
{
s.rfind( PATH_SEPARATOR )
.map( | sep_pos |
{
if sep_pos == 0
{
// We found root. We should not return string before `::`,
// as it will be empty.
Some( PATH_SEPARATOR )
}
else if sep_pos == s.len() - PATH_SEPARATOR.len()
{
// We found trailing `::`. We should continue looking for last separator.
find_parent( &s[ .. sep_pos ] )
}
else
{
Some( &s[ .. sep_pos ] )
}
})
.flatten()
}

find_parent( self.0.as_str() )
.map( | s | Self( s.to_string() ) )
}
Expand All @@ -83,41 +102,26 @@ mod private
self.0.starts_with( PATH_SEPARATOR )
}

/// Turn an absolute `Path` into a relative one by removing leading `::`.
///
/// If the `Path` is not absolute, a clone will be returned without any
/// changes.
pub fn remove_absolute( &self ) -> Path
{
if self.is_absolute()
{
Self( self.0.strip_prefix( PATH_SEPARATOR ).unwrap_or( "" ).to_string() )
}
else
{
Self( self.0.clone() )
}
}

/// Creates an owned `Path` by joining a given path to `self`.
///
/// Returns `Err(io::Error)` is the `path` is an absolute path.
/// If path is joined with an absolute path, then this absolute
/// path will be returned.
#[ inline ]
pub fn join( &self, path : &Path ) -> Result< Self, io::Error >
pub fn join( &self, path : &Path ) -> Self
{
if path.is_absolute()
{
Err( io::Error::from( io::ErrorKind::InvalidData ) )
path.clone()
}
else
{
if self.0.ends_with( PATH_SEPARATOR )
{
Ok( Self( format!( "{}{}", self.0, path.0 ) ) )
Self( format!( "{}{}", self.0, path.0 ) )
}
else
{
Ok( Self( format!( "{}::{}", self.0, path.0 ) ) )
Self( format!( "{}::{}", self.0, path.0 ) )
}
}
}
Expand All @@ -136,54 +140,6 @@ mod private
self.0
}

/// Creates a relative `Path` from an iterator over items that implement `AsRef<str>`.
/// To create an absolute `Path`, use `from_iter_abs` method.
///
/// Returns `Err(io::Error)` if the items are not valid `Path` items.
pub fn from_iter_rel< 'a >( iter : impl Iterator< Item = &'a str > ) -> Result< Self, io::Error >
{
iter.map( | path_element_str |
{
if PATH_ITEM_REGEX.is_match( path_element_str )
{
Ok ( path_element_str )
}
else
{
Err ( io::Error::from( io::ErrorKind::InvalidData ) )
}
})
.process_results( | mut item_iter |
{
Self( item_iter.join( PATH_SEPARATOR ) )
})
}

/// Creates an absolute `Path` from an iterator over strings.
/// To create a relative `Path`, use `from_iter_rel` method.
///
/// Returns `Err(io::Error)` if the items are not valid `Path` items.
pub fn from_iter_abs< 'a >( iter : impl Iterator< Item = &'a str > ) -> Result< Self, io::Error >
{
iter.map( | path_element_str |
{
if PATH_ITEM_REGEX.is_match( path_element_str )
{
Ok ( path_element_str )
}
else
{
Err ( io::Error::from( io::ErrorKind::InvalidData ) )
}
})
.process_results( | mut item_iter |
{
let mut res = item_iter.join( PATH_SEPARATOR );
res.insert_str( 0, PATH_SEPARATOR );
Self( res )
})
}

/// Iterate over components of a `Path`. If the `Path` is absolute, then the first
/// element will be `::`.
pub fn components( &self ) -> impl Iterator< Item = &str >
Expand All @@ -202,34 +158,6 @@ mod private
}
}

/// Find parent of a `Path`.
///
/// This method uses `&str` as an argument instead of `Path`
/// in order to be more general and handle trailing `::` case.
fn find_parent( s : &str ) -> Option< &str >
{
s.rfind( PATH_SEPARATOR )
.map( | sep_pos |
{
if sep_pos == 0
{
// We found root. We should not return string before `::`,
// as it will be empty.
Some( PATH_SEPARATOR )
}
else if sep_pos == s.len() - PATH_SEPARATOR.len()
{
// We found trailing `::`. We should continue looking for last separator.
find_parent( &s[ .. sep_pos ] )
}
else
{
Some( &s[ .. sep_pos ] )
}
})
.flatten()
}

impl fmt::Display for Path
{
#[ inline ]
Expand Down Expand Up @@ -305,5 +233,9 @@ mod private

crate::mod_interface!
{
own use Path;
own use
{
Path,
PATH_SEPARATOR,
};
}
Loading
Loading