use std::str::FromStr;
use crate::{Result, error};
use crate::{Checksum, Mode, Device, Time, FileType, Entry, Hash, Tree};
use crate::ownership::{Group, User};
use pathstr::PathStr;
fn parse_line(s: &str) -> Result<Entry>
{
let mut entry = Entry::default();
let mut fields: Vec<&str> = s.split(' ').collect();
if let Some(path) = fields.first()
{
entry.name = PathStr::percent_decode(*path)?;
fields.remove(0);
}
else
{
error!("failed to parse line: '{s}'")
}
let mut cksums = Vec::new();
for field in fields.into_iter()
{
match field.split_once('=')
{
Some(parts) =>
{
match parts.0
{
"type" => entry.kind = FileType::from_str(parts.1)?,
"mode" => entry.mode = Mode::from_str(parts.1)?,
"time.modify" => entry.mtime = Time::from_str(parts.1)?,
"time.change" => entry.ctime = Some(Time::from_str(parts.1)?),
"time.access" => entry.atime = Some(Time::from_str(parts.1)?),
"time.birth" => entry.btime = Some(Time::from_str(parts.1)?),
"user" => entry.user = User::from_str(parts.1)?,
"group" => entry.group = Group::from_str(parts.1)?,
#[cfg(feature = "magic")]
"mime.type" => entry.mime = Some(parts.1.to_string()),
#[cfg(feature = "magic")]
"mime.encoding" => entry.encoding = Some(parts.1.to_string()),
"inode" => entry.inode = Some(parts.1.parse::<u64>()?),
"nlink" => entry.nlink = Some(parts.1.parse::<u64>()?),
"device" => entry.device = Some(Device::from_str(parts.1)?),
"device.special" => entry.special_device = Some(Device::from_str(parts.1)?),
"link.target" => entry.link_target = Some(parts.1.to_string()),
"link.real" => entry.link_real = Some(parts.1.to_string()),
"size" => entry.size = parts.1.parse::<u64>()?,
other =>
{
if parts.0.starts_with("hash.")
&& let Some(hashparts) = parts.0.split_once('.')
&& let Ok(hash) = Hash::from_str(hashparts.1)
{
cksums.push(Checksum::new(hash, parts.1.to_string()));
}
else
{
error!("unknown key: '{other}'")
}
}
}
}
None => error!("Failed to split field by '=', got '{field}'")
}
}
if !cksums.is_empty()
{
entry.checksums = Some(cksums);
}
Ok(entry)
}
pub(crate) fn parse_tree(s: &str) -> Result<Tree>
{
let mut t = Tree::default();
for (n, line) in s.lines().into_iter().enumerate()
{
if n == 0
{
if let Some(parts) = line.split_once(' ')
{
t.time = parts.1.parse::<u64>()?;
}
else
{
error!("expected first line with '#stree <SECONDS SINCE UNIX EPOCH>'")
}
}
else if n == 1
{
let entry = parse_line(line)?;
t.top = entry.name.clone();
t.entries.push(entry);
}
else
{
t.entries.push(parse_line(line)?);
}
}
Ok(t)
}