nginx_config/
display.rs

1use ast;
2use std::fmt;
3use format::{Displayable, Formatter, Style};
4
5use value;
6
7impl Displayable for ast::Main {
8    fn display(&self, f: &mut Formatter) {
9        for item in &self.directives {
10            item.display(f);
11        }
12    }
13}
14
15impl Displayable for ast::Directive {
16    fn display(&self, f: &mut Formatter) {
17        self.item.display(f)
18    }
19}
20
21fn simple_block<D: fmt::Display>(f: &mut Formatter, name: D,
22    directives: &[ast::Directive])
23{
24    f.margin();
25    f.indent();
26    f.fmt(&format_args!("{} ", name));
27    f.start_block();
28    for dir in directives {
29        dir.display(f);
30    }
31    f.end_block();
32}
33
34fn one_arg_dir(name: &str, val: &value::Value, f: &mut Formatter) {
35    f.indent();
36    f.write(name);
37    f.write(" ");
38    val.display(f);
39    f.end();
40}
41
42impl Displayable for ast::Item {
43    fn display(&self, f: &mut Formatter) {
44        use ast::Item::*;
45        match *self {
46            | Daemon(opt)
47            | MasterProcess(opt)
48            | ProxyPassRequestHeaders(opt)
49            | ProxyPassRequestBody(opt)
50            | ProxyInterceptErrors(opt)
51            | ProxyBuffering(opt)
52            | Gzip(opt)
53            | Etag(opt)
54            | RecursiveErrorPages(opt)
55            | ChunkedTransferEncoding(opt)
56            | RealIpRecursive(opt)
57            => {
58                f.indent();
59                f.write(self.directive_name());
60                f.write(" ");
61                f.write(if opt { "on" } else { "off" });
62                f.end();
63            }
64            WorkerProcesses(ast::WorkerProcesses::Auto) => {
65                f.indent();
66                f.write("worker_processes auto");
67                f.end();
68            }
69            WorkerProcesses(ast::WorkerProcesses::Exact(n)) => {
70                f.indent();
71                f.write("worker_processes ");
72                f.fmt(&n);
73                f.end();
74            }
75            Http(ref h) => {
76                simple_block(f, "http", &h.directives);
77            }
78            Server(ref s) => {
79                simple_block(f, "server", &s.directives);
80            }
81            Location(ast::Location { ref pattern, ref directives, .. }) => {
82                simple_block(f,
83                    format_args!("location {}", pattern),
84                    &directives);
85            }
86            LimitExcept(ast::LimitExcept { ref methods, ref directives, .. })
87            => {
88                simple_block(f,
89                    format_args!("limit_except {}", methods.join(" ")),
90                    &directives);
91            }
92            Listen(ref lst) => {
93                f.indent();
94                lst.display(f);
95            }
96            ProxySetHeader { ref field, ref value } => {
97                f.indent();
98                f.write("proxy_set_header ");
99                field.display(f);
100                f.write(" ");
101                value.display(f);
102                f.end();
103            }
104            GzipStatic(opt) => {
105                f.indent();
106                f.write("gzip_static ");
107                f.write(opt.as_str());
108                f.end();
109            }
110            GzipProxied(ref opt) => {
111                f.indent();
112                f.write("gzip_proxied");
113                for item in opt {
114                    f.write(" ");
115                    f.write(item.as_str());
116                }
117                f.end();
118            }
119            AddHeader(ref h) => {
120                f.indent();
121                f.write("add_header ");
122                h.field.display(f);
123                f.write(" ");
124                h.value.display(f);
125                if h.always {
126                    f.write(" always");
127                }
128                f.end();
129            }
130            ServerName(ref items) => {
131                use ast::ServerName::*;
132                f.indent();
133                f.write("server_name");
134                for item in items {
135                    match *item {
136                        Exact(ref v)
137                        => f.fmt(&format_args!(" {}", escape(&v))),
138                        Suffix(ref v)
139                        => f.fmt(&format_args!(" .{}", escape(&v))),
140                        StarSuffix(ref v)
141                        => f.fmt(&format_args!(" *.{}", escape(&v))),
142                        StarPrefix(ref v)
143                        => f.fmt(&format_args!(" {}.*", escape(&v))),
144                        Regex(ref v)
145                        => f.fmt(&format_args!(" ~{}", escape(&v))),
146                    }
147                }
148                f.end();
149            }
150            Set { ref variable, ref value } => {
151                f.indent();
152                f.write("set $");
153                f.write(variable); // TODO(tailhook) check syntax?
154                f.write(" ");
155                value.display(f);
156                f.end();
157            }
158            Map(ref m) => {
159                use ast::MapPattern::*;
160                f.margin();
161                f.indent();
162                f.write("map ");
163                m.expression.display(f);
164                f.write(" $");
165                f.write(&m.variable); // TODO(tailhook) check syntax?
166                f.write(" ");
167                f.start_block();
168                if m.volatile {
169                    f.indent();
170                    f.write("volatile");
171                    f.end();
172                }
173                if m.hostnames {
174                    f.indent();
175                    f.write("hostnames");
176                    f.end();
177                }
178                if let Some(ref def) = m.default {
179                    f.indent();
180                    f.write("default ");
181                    def.display(f);
182                    f.end();
183                }
184                for inc in &m.includes {
185                    f.indent();
186                    f.write("include ");
187                    f.write(escape(inc));
188                    f.end();
189                }
190                for &(ref pat, ref value) in &m.patterns {
191                    f.indent();
192                    match *pat {
193                        Exact(ref v) if matches!(&v[..],
194                            | "volatile"
195                            | "hostnames"
196                            | "default"
197                            | "include"
198                        ) => f.fmt(&format_args!("\\{}", escape(&v))),
199                        Exact(ref v)
200                        => f.fmt(&format_args!("{}", escape(&v))),
201                        Suffix(ref v)
202                        => f.fmt(&format_args!(".{}", escape(&v))),
203                        StarSuffix(ref v)
204                        => f.fmt(&format_args!("*.{}", escape(&v))),
205                        StarPrefix(ref v)
206                        => f.fmt(&format_args!("{}.*", escape(&v))),
207                        Regex(ref v)
208                        => f.fmt(&format_args!("~{}", escape(&v))),
209                    }
210                    f.write(" ");
211                    value.display(f);
212                    f.end();
213                }
214                f.end_block();
215            }
216            Rewrite(ref rw) => {
217                use ast::RewriteFlag::*;
218                f.indent();
219                f.write("rewrite ");
220                f.write(escape(&rw.regex));
221                f.write(" ");
222                rw.replacement.display(f);
223                f.write(match rw.flag {
224                    Some(Last) => " last",
225                    Some(Break) => " break",
226                    Some(Redirect) => " redirect",
227                    Some(Permanent) => " permanent",
228                    None => "",
229                });
230                f.end();
231            }
232            | Root(ref val)
233            | Alias(ref val)
234            | DefaultType(ref val)
235            | ClientMaxBodySize(ref val)
236            | Include(ref val)
237            | RewriteByLuaFile(ref val)
238            | BalancerByLuaFile(ref val)
239            | AccessByLuaFile(ref val)
240            | HeaderFilterByLuaFile(ref val)
241            | ContentByLuaFile(ref val)
242            | BodyFilterByLuaFile(ref val)
243            | LogByLuaFile(ref val)
244            | LuaNeedRequestBody(ref val)
245            | SslCertificateByLuaFile(ref val)
246            | SslSessionFetchByLuaFile(ref val)
247            | SslSessionStoreByLuaFile(ref val)
248            | SslCertificate(ref val)
249            | SslCertificateKey(ref val)
250            | ProxyPass(ref val)
251            | ProxyCache(ref val)
252            | ProxyCacheKey(ref val)
253            | ProxyMethod(ref val)
254            | ProxyReadTimeout(ref val)
255            | ProxyConnectTimeout(ref val)
256            | ProxyHideHeader(ref val)
257            | ProxyPassHeader(ref val)
258            | ProxyNextUpstreamTries(ref val)
259            | ProxyNextUpstreamTimeout(ref val)
260            | ServerTokens(ref val)
261            | RealIpHeader(ref val)
262            => {
263                one_arg_dir(self.directive_name(), val, f);
264            }
265            | EmptyGif
266            | Internal
267            => {
268                f.indent();
269                f.write(self.directive_name());
270                f.end();
271            }
272            ErrorPage(ref ep) => {
273                use ast::ErrorPageResponse::*;
274                f.indent();
275                f.write("error_page");
276                for code in &ep.codes {
277                    f.write(" ");
278                    f.fmt(code);
279                }
280                match ep.response_code {
281                    Target => {},
282                    Replace(ref code) =>  { f.write(" ="); f.fmt(code); }
283                    Redirect(ref code) => { f.write(" ="); f.fmt(code); }
284                    Keep => { f.write(" ="); }
285                }
286                f.write(" ");
287                ep.uri.display(f);
288                f.end()
289            }
290            Return(ref ret) => {
291                use ast::Return::*;
292                f.indent();
293                f.write("return ");
294                match ret {
295                    Redirect { code: None, url } => url.display(f),
296                    Redirect { code: Some(code), url } => {
297                        f.fmt(&code);
298                        f.write(" ");
299                        url.display(f);
300                    }
301                    Text { code, text } => {
302                        f.fmt(&code);
303                        match text {
304                            Some(v) => {
305                                f.write(" ");
306                                v.display(f);
307                            }
308                            None => {}
309                        }
310                    }
311                }
312                f.end()
313            }
314            TryFiles(ref tf) => {
315                use ast::TryFilesLastOption::*;
316                f.indent();
317                f.write("try_files ");
318                for item in &tf.options {
319                    item.display(f);
320                    f.write(" ");
321                }
322                match tf.last_option {
323                    Uri(ref v) => v.display(f),
324                    NamedLocation(ref loc) => {
325                        f.write("@");
326                        f.write(&loc);
327                    }
328                    Code(code) => {
329                        f.write("=");
330                        f.fmt(&code);
331                    }
332                }
333                f.end();
334            }
335            Expires(::ast::Expires { modified, ref value }) => {
336                f.indent();
337                f.write("expires ");
338                if modified {
339                    f.write("modified ");
340                }
341                value.display(f);
342                f.end();
343            }
344            If(ast::If { ref condition, ref directives, position: _ }) => {
345                use ast::IfCondition::*;
346                f.indent();
347                f.write("if (");
348                match condition {
349                    NonEmpty(ref v) => v.display(f),
350                    Eq(ref v, ref s) => {
351                        v.display(f);
352                        f.write(" = ");
353                        f.write(&escape(s));
354                    }
355                    Neq(ref v, ref s) => {
356                        v.display(f);
357                        f.write(" != ");
358                        f.write(&escape(s));
359                    }
360                    RegEq(ref v, ref r, case) => {
361                        v.display(f);
362                        if *case {
363                            f.write(" ~ ");
364                        } else {
365                            f.write(" ~* ");
366                        }
367                        f.write(&escape(r));
368                    }
369                    RegNeq(ref v, ref r, case) => {
370                        v.display(f);
371                        if *case {
372                            f.write(" !~ ");
373                        } else {
374                            f.write(" !~* ");
375                        }
376                        f.write(&escape(r));
377                    }
378                    Exists(ref v) => {
379                        f.write("-e ");
380                        v.display(f);
381                    }
382                    NotExists(ref v) => {
383                        f.write("!-e ");
384                        v.display(f);
385                    },
386                    FileExists(ref v) => {
387                        f.write("-f ");
388                        v.display(f);
389                    },
390                    FileNotExists(ref v) => {
391                        f.write("!-f ");
392                        v.display(f);
393                    },
394                    DirExists(ref v) => {
395                        f.write("-d ");
396                        v.display(f);
397                    },
398                    DirNotExists(ref v) => {
399                        f.write("!-d ");
400                        v.display(f);
401                    },
402                    Executable(ref v) => {
403                        f.write("-x ");
404                        v.display(f);
405                    },
406                    NotExecutable(ref v) => {
407                        f.write("!-x ");
408                        v.display(f);
409                    },
410                }
411                f.write(") ");
412                f.start_block();
413                for dir in directives {
414                    dir.display(f);
415                }
416                f.end_block();
417            }
418            Allow(ref source) | Deny(ref source) => {
419                use ast::Source::*;
420                f.indent();
421                f.write(self.directive_name());
422                f.write(" ");
423                match source {
424                    All => f.write("all"),
425                    Unix => f.write("unix:"),
426                    Ip(ip) => f.fmt(ip),
427                    Network(ip, bits) => {
428                        f.fmt(ip);
429                        f.write("/");
430                        f.fmt(bits);
431                    }
432                }
433                f.end();
434            }
435            ProxyHttpVersion(ver) => {
436                use ast::ProxyHttpVersion::*;
437                f.indent();
438                match ver {
439                    V1_0 => f.write("proxy_http_version 1.0"),
440                    V1_1 => f.write("proxy_http_version 1.1"),
441                }
442                f.end();
443            }
444            ProxyIgnoreHeaders(ref headers) => {
445                f.indent();
446                f.write(self.directive_name());
447                for h in headers {
448                    f.write(" ");
449                    f.write(&escape(h));
450                }
451                f.end();
452            }
453            ProxyCacheValid(ref val) => {
454                use ast::ProxyCacheValid::*;
455                f.indent();
456                f.write(self.directive_name());
457                match val {
458                    Normal(ref val) => {
459                        f.write(" ");
460                        val.display(f);
461                    }
462                    Specific(ref codes, ref val) => {
463                        for code in codes {
464                            f.write(" ");
465                            f.fmt(&code);
466                        }
467                        f.write(" ");
468                        val.display(f);
469                    }
470                    Any(ref val) => {
471                        f.write(" any ");
472                        val.display(f);
473                    }
474                }
475                f.end();
476            }
477            KeepaliveTimeout(ref timeo, ref header_timeo) => {
478                f.indent();
479                f.write(self.directive_name());
480                f.write(" ");
481                timeo.display(f);
482                if let Some(header_timeo) = header_timeo {
483                    f.write(" ");
484                    header_timeo.display(f);
485                }
486                f.end();
487            }
488            ProxyNextUpstream(ref items) => {
489                use ast::ProxyNextUpstreamFlag::*;
490                f.indent();
491                f.write(self.directive_name());
492                for item in items {
493                    f.write(" ");
494                    f.write(match item {
495                        Error => "error",
496                        Timeout => "timeout",
497                        InvalidHeader => "invalid_header",
498                        Http500 => "http_500",
499                        Http502 => "http_502",
500                        Http503 => "http_503",
501                        Http504 => "http_504",
502                        Http403 => "http_403",
503                        Http404 => "http_404",
504                        Http429 => "http_429",
505                        NonIdempotent => "non_idempotent",
506                        Off => "off",
507                    });
508                }
509                f.end();
510            }
511            AccessLog(ast::AccessLog::Off) =>  {
512                f.indent();
513                f.write("access_log off");
514                f.end();
515            }
516            AccessLog(ast::AccessLog::On(ref lg)) =>  {
517                f.indent();
518                f.write("access_log ");
519                lg.path.display(f);
520                if let Some(ref fmt) = lg.format {
521                    f.write(" ");
522                    f.fmt(&escape(fmt));
523                }
524                if let Some(ref buf) = lg.buffer {
525                    f.write(" buffer=");
526                    f.fmt(&escape(buf));
527                }
528                if let Some(ref gzip) = lg.gzip {
529                    if let Some(level) = gzip {
530                        f.write(" gzip=");
531                        f.fmt(&level);
532                    } else {
533                        f.write(" gzip");
534                    }
535                }
536                if let Some(ref flush) = lg.flush {
537                    f.write(" flush=");
538                    f.fmt(&escape(flush));
539                }
540                if let Some(ref condition) = lg.condition {
541                    f.write(" if=");
542                    condition.display(f);
543                }
544                f.end();
545            }
546            SetRealIpFrom(ref source) => {
547                use ast::RealIpFrom::*;
548                f.indent();
549                f.write(self.directive_name());
550                f.write(" ");
551                match source {
552                    Unix => f.write("unix:"),
553                    Ip(ip) => f.fmt(ip),
554                    Network(ip, bits) => {
555                        f.fmt(ip);
556                        f.write("/");
557                        f.fmt(bits);
558                    }
559                }
560                f.end();
561            }
562            ErrorLog { ref file, level } => {
563                f.indent();
564                f.write(self.directive_name());
565                f.write(" ");
566                file.display(f);
567                if let Some(level) = level {
568                    use ast::ErrorLevel::*;
569                    f.write(" ");
570                    f.write(match level {
571                        Debug => "debug",
572                        Info => "info",
573                        Notice => "notice",
574                        Warn => "warn",
575                        Error => "error",
576                        Crit => "crit",
577                        Alert => "alert",
578                        Emerg => "emerg",
579                    });
580                }
581                f.end();
582            }
583            Index(ref items) => {
584                f.indent();
585                f.write("index");
586                for item in items {
587                    f.write(" ");
588                    item.display(f);
589                }
590                f.end();
591            }
592        }
593    }
594}
595
596impl Displayable for ast::Listen {
597    fn display(&self, f: &mut Formatter) {
598        f.write("listen ");
599        self.address.display(f);
600        if self.default_server { f.write(" default_server") }
601        if self.ssl { f.write(" ssl") }
602        match self.ext {
603            Some(ast::HttpExt::Http2) => f.write(" http2"),
604            Some(ast::HttpExt::Spdy) => f.write(" spdy"),
605            None => {}
606        }
607        if self.proxy_protocol { f.write(" proxy_protocol") }
608        if let Some(setfib) = self.setfib {
609            f.fmt(&format_args!(" setfib={}", setfib));
610        }
611        if let Some(fastopen) = self.fastopen {
612            f.fmt(&format_args!(" fastopen={}", fastopen));
613        }
614        if let Some(backlog) = self.backlog {
615            f.fmt(&format_args!(" backlog={}", backlog));
616        }
617        if let Some(rcvbuf) = self.rcvbuf {
618            f.fmt(&format_args!(" rcvbuf={}", rcvbuf));
619        }
620        if let Some(sndbuf) = self.sndbuf {
621            f.fmt(&format_args!(" sndbuf={}", sndbuf));
622        }
623        if self.deferred { f.write(" deferred") }
624        if self.bind { f.write(" bind") }
625        if let Some(ipv6only) = self.ipv6only {
626            f.fmt(&format_args!(" ipv6only={}",
627                               if ipv6only { "on" } else { "off" }));
628        }
629        if self.reuseport { f.write(" reuseport") }
630        f.end();
631    }
632}
633
634impl Displayable for ast::Address {
635    fn display(&self, f: &mut Formatter) {
636        use ast::Address::*;
637        match *self {
638            Ip(sa) => f.fmt(&sa),
639            StarPort(p) => f.fmt(&format_args!("*:{}", p)),
640            Port(p) => f.fmt(&p),
641            // TODO(tailhook) escape path
642            Unix(ref path) => f.fmt(&format_args!("unix:{}", path.display())),
643        }
644    }
645}
646
647fn to_string<T: Displayable>(v: &T) -> String {
648    let style = Style::default();
649    let mut formatter = Formatter::new(&style);
650    v.display(&mut formatter);
651    formatter.into_string()
652}
653
654macro_rules! impl_display {
655    ($( $typ: ty, )+) => {
656        $(
657            impl fmt::Display for $typ {
658                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
659                    f.write_str(&to_string(self))
660                }
661            }
662        )+
663    };
664}
665
666impl_display!(
667    ast::Main,
668    ast::Listen,
669    ast::Address,
670    ast::Directive,
671    ast::Item,
672    value::Value,
673);
674
675fn escape(s: &str) -> &str {
676    // TODO(tailhook) escape raw value
677    return s
678}
679
680impl fmt::Display for ast::LocationPattern {
681    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
682        use ast::LocationPattern::*;
683        match *self {
684            Prefix(ref p) => f.write_str(escape(p)),
685            Exact(ref p) => write!(f, "= {}", escape(p)),
686            FinalPrefix(ref p) => write!(f, "^~ {}", escape(p)),
687            Regex(ref p) => write!(f, "~ {}", escape(p)),
688            RegexInsensitive(ref p) => write!(f, "~* {}", escape(p)),
689            Named(ref name) => {
690                write!(f, "{}", escape(&(String::from("@") + name)))
691            }
692        }
693    }
694}
695
696impl ast::GzipStatic {
697    fn as_str(&self) -> &str {
698        use ast::GzipStatic::*;
699        match *self {
700            On => "on",
701            Off => "off",
702            Always => "always",
703        }
704    }
705}
706
707impl ast::GzipProxied {
708    fn as_str(&self) -> &str {
709        use ast::GzipProxied::*;
710        match *self {
711            Off => "off",
712            Expired => "expired",
713            NoCache => "no-cache",
714            NoStore => "no-store",
715            Private => "private",
716            NoLastModified => "no_last_modified",
717            NoEtag => "no_etag",
718            Auth => "auth",
719            Any => "any",
720        }
721    }
722}
723
724
725impl fmt::Display for ast::GzipStatic {
726    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
727        self.as_str().fmt(f)
728    }
729}
730
731impl fmt::Display for ast::GzipProxied {
732    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
733        self.as_str().fmt(f)
734    }
735}