use std::{
cell::RefCell,
collections::HashMap,
fmt,
rc::{Rc, Weak},
};
use qvnt::qasm::Int;
use crate::utils;
#[derive(Debug)]
pub struct IntTree<'t> {
head: RefCell<Option<Rc<String>>>,
map: HashMap<Rc<String>, (Weak<String>, Int<'t>)>,
}
pub enum RemoveStatus {
Removed,
NotFound,
IsParent,
IsHead,
}
impl<'t> IntTree<'t>
where
Self: 't,
{
pub fn with_root<S: ToString>(root: S) -> Self {
let root = Rc::new(root.to_string());
let map = HashMap::from([(Rc::clone(&root), (Weak::new(), Int::default()))]);
Self {
head: RefCell::new(Some(root)),
map,
}
}
pub fn keys(&self) -> Vec<(Rc<String>, Rc<String>)> {
self.map
.iter()
.filter_map(|(a, (b, _))| Some((Rc::clone(a), Weak::upgrade(b)?)))
.collect()
}
pub fn commit<S: AsRef<str>>(&mut self, tag: S, change: Int<'t>) -> bool {
let tag = tag.as_ref().to_string();
if self.map.contains_key(&tag) {
return false;
}
let tag = Rc::new(tag);
let old_head = match &*self.head.borrow() {
Some(rc) => Rc::downgrade(rc),
None => Weak::new(),
};
*self.head.borrow_mut() = Some(Rc::clone(&tag));
self.map.insert(tag, (old_head, change));
true
}
pub fn checkout<S: AsRef<str>>(&self, tag: S) -> bool {
let tag = tag.as_ref().to_string();
match self.map.get_key_value(&tag) {
Some(entry) => {
*self.head.borrow_mut() = Some(Rc::clone(entry.0));
true
}
None => false,
}
}
pub fn collect_to_head(&self) -> Option<Int<'t>> {
let mut start = Rc::clone(self.head.borrow().as_ref()?);
let mut int_changes = Int::<'t>::default();
loop {
let curr = self.map.get(&start)?.clone();
int_changes = unsafe { int_changes.prepend_int(curr.1.clone()) };
if let Some(next) = Weak::upgrade(&curr.0) {
start = Rc::clone(&next);
} else {
break Some(int_changes);
}
}
}
pub fn remove<S: AsRef<str>>(&mut self, tag: S) -> RemoveStatus {
let tag = tag.as_ref().to_string();
if self.head.borrow().as_deref() == Some(&tag) {
return RemoveStatus::IsHead;
}
let mut is_presented = false;
for tags in self.map.iter() {
if **tags.0 == tag {
is_presented = true;
}
if let Some(par_tag) = Weak::upgrade(&tags.1 .0) {
if *par_tag == tag {
return RemoveStatus::IsParent;
}
}
}
if !is_presented {
return RemoveStatus::NotFound;
}
let removed = self.map.remove(&tag).unwrap().1;
<Int<'t> as utils::drop_leakage::DropExt>::drop(removed);
RemoveStatus::Removed
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
UnknownTagCmd(String),
UnspecifiedTag,
}
impl From<Error> for crate::lines::Error {
fn from(e: Error) -> Self {
Self::TagError(e)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::UnknownTagCmd(cmd) => write!(f, "Unknown Tag subcommand {cmd:?}"),
Error::UnspecifiedTag => write!(f, "Tag name should be specified"),
}
}
}
impl std::error::Error for Error {}
pub const HELP: &str = "QVNT Interpreter Tag command
USAGE:
:tag [TAGCMD...]
TAGCMD:
ls Show the list of previously created tags
mk TAG Create TAG with current state
ch TAG Swap current state to TAG's state
rm TAG Remove TAG from tree
root Swap current state to default state
help|h|? Show this reference
";
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Command {
List,
Create(String),
Remove(String),
Checkout(String),
Root,
Help,
}
impl Command {
pub fn parse_command<'a, I: Iterator<Item = &'a str>>(
source: &mut I,
) -> Result<Command, Error> {
match source.next() {
None | Some("ls") => Ok(Command::List),
Some("mk") => match source.next() {
Some(arg) => Ok(Command::Create(arg.to_string())),
None => Err(Error::UnspecifiedTag),
},
Some("rm") => match source.next() {
Some(arg) => Ok(Command::Remove(arg.to_string())),
None => Err(Error::UnspecifiedTag),
},
Some("ch") => match source.next() {
Some(arg) => Ok(Command::Checkout(arg.to_string())),
None => Err(Error::UnspecifiedTag),
},
Some("root") => Ok(Command::Root),
Some("help" | "h" | "?") => Ok(Command::Help),
Some(cmd) => Err(Error::UnknownTagCmd(cmd.to_string())),
}
}
}