Skip to content

Commit

Permalink
READY : Agents (#1500)
Browse files Browse the repository at this point in the history
* `parent`

* Simplify context

* Remove redunant `from_iter`

* Fix joining of paths

* Start working on `ScenarioProcessed`

* Implement `ScenarioProcessed`
  • Loading branch information
InAnYan authored Dec 3, 2024
1 parent 2e7e3de commit f8ab967
Show file tree
Hide file tree
Showing 10 changed files with 218 additions and 264 deletions.
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

0 comments on commit f8ab967

Please sign in to comment.