Skip to content
Open
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
150 changes: 140 additions & 10 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ where
pub name: T,
pub args: Vec<T>,
pub children: Option<Vec<Directive<S, T>>>,
pub(crate) _scheme: PhantomData<S>,
pub _scheme: PhantomData<S>,
pub is_comment: bool,
pub newline: bool,
}

impl<S: Debug, T: Debug> Debug for Directive<S, T>
Expand All @@ -110,7 +112,9 @@ where
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut ds = f.debug_struct("Directive");
ds.field("name", &self.name).field("args", &self.args);
ds.field("name", &self.name)
.field("args", &self.args)
.field("is_comment", &self.is_comment);
if let Some(children) = self.children.as_ref() {
ds.field("children", children);
}
Expand Down Expand Up @@ -143,31 +147,83 @@ where
S: Clone + Default,
T: FromLiteral + AsRef<str>,
{
pub fn query(&self, path: &str) -> Vec<Self> {
pub fn query(&mut self, path: &str) -> Vec<&mut Self> {
let mut result = vec![];
if let Some(childs) = self.children.as_ref() {
Self::inner_query(childs, path, &mut result);
}
Self::inner_query(vec![self], path, &mut result);
result
}

fn inner_query(dirs: &[Self], path: &str, out: &mut Vec<Self>) {
fn inner_query<'a>(dirs: Vec<&'a mut Self>, path: &str, out: &mut Vec<&'a mut Self>) {
let mut pathitem = path;
let mut rest = None;
if let Some((i, r)) = path.split_once("/") {
pathitem = i;
rest.replace(r);
}
for d in dirs.iter() {
for d in dirs {
if d.name.as_ref().eq_ignore_ascii_case(pathitem) {
if let Some(path) = rest {
Self::inner_query(
d.children.as_ref().map(Vec::as_slice).unwrap_or(&[]),
d.children.iter_mut().flatten().collect::<Vec<_>>(),
path,
out,
);
} else {
out.push(d.clone());
out.push(d);
}
}
}
}

pub fn remove(&mut self, path: &str, values: Option<&[&str]>) -> &mut Self {
self.inner_remove(path, values);
self
}

fn inner_remove(&mut self, path: &str, values: Option<&[&str]>) {
let mut pathitem = path;
let mut rest = None;

if let Some((i, r)) = path.split_once("/") {
pathitem = i;
rest.replace(r);
}

if self.name.as_ref().eq_ignore_ascii_case(pathitem)
&& self.children.is_some()
&& rest.is_some()
{
let rest = rest.unwrap();
let mut i = 0;
let childs = self.children.as_mut().unwrap();
while i < childs.len() {
let c = childs[i].as_mut();
let mut removed = false;

if rest.split_once("/").is_some() {
c.remove(rest, values);
} else if c.name.as_ref().eq_ignore_ascii_case(rest) {
let mut all_same = true;
if values.is_some() {
let values = values.unwrap();
if values.len() == c.args.len() {
let mut ia = c.args.iter();
values.iter().any(|x| {
all_same = ia.next().unwrap().as_ref().contains(x);
!all_same
});
} else {
all_same = false;
}
}
if all_same {
childs.remove(i);
removed = true;
}
}

if !removed {
i += 1;
}
}
}
Expand Down Expand Up @@ -212,3 +268,77 @@ where
}
}
}

impl<S> Directive<S, String>
where
S: Clone + Default,
{
pub fn to_string(&self, depth: usize) -> std::string::String {
let mut result = String::new();

if self.newline {
result += "\n";
result += &"\t".repeat(depth - 1);
}

if self.children.is_some() {
result += "\n";
result += &"\t".repeat(depth - 1);
}

// special for '' $http_connection
let name = if self.name.is_empty() && !self.args.is_empty() {
"''".to_string()
} else {
self.name.clone()
};

if self.is_comment {
result += &format!("#{name}\n");
return result;
}

result += name.as_str();
let mut need_com = false;
if self.args.len() > 0 {
result += " ";
result += self.args.join(" ").as_str();
if let Some(last) = self.args.last() {
need_com = !last.ends_with(";");
}
}

if let Some(children) = &self.children {
if !self.name.is_empty() {
result += " {";
if children.len() > 0 {
result += children
.iter()
.map(|x| "\t".repeat(depth).to_string() + &x.to_string(depth + 1))
.collect::<String>()
.as_str();
} else {
result += "\n";
}
result += ("\t".repeat(depth - 1).to_string() + "}\n").as_str();
} else {
if need_com {
result += ";";
}
if children.len() > 0 {
result += children
.iter()
.map(|x| x.to_string(depth))
.collect::<String>()
.as_str();
}
}
} else {
if need_com {
result += ";";
}
result += "\n";
}
result
}
}
43 changes: 23 additions & 20 deletions src/nginx/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub enum Token<'a> {
NewLine,
Eof,
Literal(Literal<'a>),
Comment(Literal<'a>),
}

impl<'a> Token<'a> {
Expand Down Expand Up @@ -62,18 +63,6 @@ fn semicolon(input: &[u8]) -> IResult<&[u8], Token> {
value(Token::Semicolon, tag(b";"))(input)
}

fn comment(input: &[u8]) -> IResult<&[u8], &[u8]> {
map(
tuple((
tag("#"),
take_till(is_newline),
opt(tag(b"\r")),
opt(tag(b"\n")),
)),
|x| x.1,
)(input)
}

fn literal(input: &[u8]) -> IResult<&[u8], Token> {
let (_, mut first) = be_u8(input)?;

Expand Down Expand Up @@ -105,30 +94,44 @@ fn literal(input: &[u8]) -> IResult<&[u8], Token> {
_ => {
first = 0;
map_res(
escaped(none_of(" \t\r\n;'\"\\"), '\\', anychar),
escaped(none_of("{ \t\r\n;'\"\\"), '\\', anychar),
std::str::from_utf8,
)(input)
}
}?;
Ok((input, Token::Literal(Literal { raw, quote: first })))
}

fn tocomment(input: &[u8]) -> IResult<&[u8], Token> {
map(tuple((tag("#"), take_till(is_newline))), |x| {
Token::Comment(Literal {
raw: std::str::from_utf8(x.1).unwrap_or_default(),
quote: b'#',
})
})(input)
}

pub fn tokenizer(mut input: &[u8]) -> IResult<&[u8], Token> {
loop {
let (rest, cmt) = space_and_comment(input)?;
let (rest, cmt) = multispace0(input)?;
// println!("{cmt:?}");
// if let Some(c) = cmt.clone() {
// // println!("{}", String::from_utf8_lossy(rest).to_string());
// println!("{}", String::from_utf8(c).unwrap_or_default());
// }
// println!("{} {} {} - {:?}", rest.len(), cmt.len(), input.len(), cmt);
// println!("{}", String::from_utf8_lossy(cmt).to_string());
input = rest;
if cmt.is_none() {
if cmt.len() == 0 {
break;
}
}

// println!("{}", String::from_utf8_lossy(input).to_string());

if input.len() == 0 {
return Ok((input, Token::Eof));
}

alt((starttag, endtag, semicolon, literal))(input)
}

fn space_and_comment(input: &[u8]) -> IResult<&[u8], Option<&[u8]>> {
map(tuple((multispace0, opt(comment))), |x| x.1)(input)
alt((starttag, endtag, tocomment, semicolon, literal))(input)
}
60 changes: 47 additions & 13 deletions src/nginx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,27 @@ pub struct Nginx;

impl DirectiveTrait<Nginx> for Directive<Nginx> {
fn parse(input: &[u8]) -> anyhow::Result<Vec<Self>> {
let res = parse_block(input).map_err(|err| {
err.map(|e| {
let errs = e
.errors
.iter()
.map(|(i, code)| {
let ((l, c), pos) = line_column2(input, i).unwrap();
format!("0x{pos:x}({l}:{c}) err: {:?}", code)
})
.collect::<Vec<_>>();
anyhow::anyhow!("{}", errs.join("\n"))
let res = parse_block(input)
.map(|(i, r)| {
let r = Directive {
children: Some(r),
..Directive::default()
};
(i, vec![r])
})
})?;
.map_err(|err| {
err.map(|e| {
let errs = e
.errors
.iter()
.map(|(i, code)| {
let ((l, c), pos) = line_column2(input, i).unwrap();
format!("0x{pos:x}({l}:{c}) err: {:?}", code)
})
.collect::<Vec<_>>();
anyhow::anyhow!("{}", errs.join("\n"))
})
})?;
Ok(res.1)
}

Expand Down Expand Up @@ -64,7 +72,14 @@ impl DirectiveTrait<Nginx> for Directive<Nginx> {
.flatten()
{
let data = std::fs::read(res.resolve(&path)?)?;
for c in Self::parse(&data).with_context(|| format!("parse {path:?}"))? {
let mut sub = Self::parse(&data)
.with_context(|| format!("parse {path:?}"))?;
let sub = sub
.iter_mut()
.flat_map(|x|x.children.as_mut().cloned())
.flatten()
.collect::<Vec<_>>();
for c in sub {
c.resolve_include_inner(dir, out, res)?;
}
}
Expand All @@ -87,6 +102,7 @@ fn parse_literal(input: &[u8]) -> IResult<&[u8], Literal<'_>> {

fn parse_block(mut input: &[u8]) -> IResult<&[u8], Vec<Directive<Nginx>>> {
let mut result = vec![];
let mut before_is_literal = None;
loop {
let mut d = Directive::default();
let (rest, tag) = tokenizer(input).map_err(|err| {
Expand All @@ -96,12 +112,29 @@ fn parse_block(mut input: &[u8]) -> IResult<&[u8], Vec<Directive<Nginx>>> {
let lit = match tag {
Token::Literal(lit) => lit,
Token::BlockEnd | Token::Eof => break,
Token::Comment(c) => {
d.is_comment = true;
d.name = c.raw.to_string();
if before_is_literal != Some(false) {
before_is_literal = Some(false);
d.newline = true;
}
result.push(d);
input = rest;
continue;
}
_ => return fail(input),
};

if before_is_literal != Some(true) {
d.newline = true;
}
before_is_literal = Some(true);
d.name = lit.clone().into();
let (rest, args) = map(many0(parse_literal), |v| {
v.into_iter().map(Into::into).collect()
})(rest)?;

d.args = args;

let (rest, tok) = tokenizer(rest)?;
Expand Down Expand Up @@ -140,6 +173,7 @@ fn parse_block(mut input: &[u8]) -> IResult<&[u8], Vec<Directive<Nginx>>> {
VerboseError::from_error_kind(input, nom::error::ErrorKind::Fail),
)));
}
d.newline = false;
input = rest;
}
_ => {
Expand Down