1 |
yakumo_izuru |
1.1 |
package gcss |
2 |
|
|
|
3 |
|
|
import ( |
4 |
|
|
"bytes" |
5 |
|
|
"fmt" |
6 |
|
|
"io" |
7 |
|
|
"strings" |
8 |
|
|
) |
9 |
|
|
|
10 |
|
|
// declaration represents a declaration of CSS. |
11 |
|
|
type declaration struct { |
12 |
|
|
elementBase |
13 |
|
|
property string |
14 |
|
|
value string |
15 |
|
|
} |
16 |
|
|
|
17 |
|
|
// WriteTo writes the declaration to the writer. |
18 |
|
|
func (dec *declaration) WriteTo(w io.Writer) (int64, error) { |
19 |
|
|
return dec.writeTo(w, nil) |
20 |
|
|
} |
21 |
|
|
|
22 |
|
|
// writeTo writes the declaration to the writer. |
23 |
|
|
func (dec *declaration) writeTo(w io.Writer, params map[string]string) (int64, error) { |
24 |
|
|
bf := new(bytes.Buffer) |
25 |
|
|
|
26 |
|
|
bf.WriteString(dec.property) |
27 |
|
|
bf.WriteString(colon) |
28 |
|
|
|
29 |
|
|
for i, v := range strings.Split(dec.value, space) { |
30 |
|
|
if i > 0 { |
31 |
|
|
bf.WriteString(space) |
32 |
|
|
} |
33 |
|
|
|
34 |
|
|
for j, w := range strings.Split(v, comma) { |
35 |
|
|
if j > 0 { |
36 |
|
|
bf.WriteString(comma) |
37 |
|
|
} |
38 |
|
|
|
39 |
|
|
if strings.HasPrefix(w, dollarMark) { |
40 |
|
|
// Writing to the bytes.Buffer never returns an error. |
41 |
|
|
dec.writeParamTo(bf, strings.TrimPrefix(w, dollarMark), params) |
42 |
|
|
} else { |
43 |
|
|
bf.WriteString(w) |
44 |
|
|
} |
45 |
|
|
} |
46 |
|
|
} |
47 |
|
|
|
48 |
|
|
bf.WriteString(semicolon) |
49 |
|
|
|
50 |
|
|
n, err := w.Write(bf.Bytes()) |
51 |
|
|
|
52 |
|
|
return int64(n), err |
53 |
|
|
} |
54 |
|
|
|
55 |
|
|
// writeParam writes the param to the writer. |
56 |
|
|
func (dec *declaration) writeParamTo(w io.Writer, name string, params map[string]string) (int64, error) { |
57 |
|
|
if s, ok := params[name]; ok { |
58 |
|
|
if strings.HasPrefix(s, dollarMark) { |
59 |
|
|
if v, ok := dec.Context().vars[strings.TrimPrefix(s, dollarMark)]; ok { |
60 |
|
|
return v.WriteTo(w) |
61 |
|
|
} |
62 |
|
|
return 0, nil |
63 |
|
|
} |
64 |
|
|
|
65 |
|
|
n, err := w.Write([]byte(s)) |
66 |
|
|
return int64(n), err |
67 |
|
|
} |
68 |
|
|
|
69 |
|
|
if v, ok := dec.Context().vars[name]; ok { |
70 |
|
|
return v.WriteTo(w) |
71 |
|
|
} |
72 |
|
|
|
73 |
|
|
return 0, nil |
74 |
|
|
} |
75 |
|
|
|
76 |
|
|
// declarationPV extracts a declaration property and value |
77 |
|
|
// from the line. |
78 |
|
|
func declarationPV(ln *line) (string, string, error) { |
79 |
|
|
pv := strings.SplitN(strings.TrimSpace(ln.s), space, 2) |
80 |
|
|
|
81 |
|
|
if len(pv) < 2 { |
82 |
|
|
return "", "", fmt.Errorf("declaration's property and value should be divided by a space [line: %d]", ln.no) |
83 |
|
|
} |
84 |
|
|
|
85 |
|
|
if !strings.HasSuffix(pv[0], colon) { |
86 |
|
|
return "", "", fmt.Errorf("property should end with a colon [line: %d]", ln.no) |
87 |
|
|
} |
88 |
|
|
|
89 |
|
|
return strings.TrimSuffix(pv[0], colon), pv[1], nil |
90 |
|
|
} |
91 |
|
|
|
92 |
|
|
// newDeclaration creates and returns a declaration. |
93 |
|
|
func newDeclaration(ln *line, parent element) (*declaration, error) { |
94 |
|
|
property, value, err := declarationPV(ln) |
95 |
|
|
|
96 |
|
|
if err != nil { |
97 |
|
|
return nil, err |
98 |
|
|
} |
99 |
|
|
|
100 |
|
|
if strings.HasSuffix(value, semicolon) { |
101 |
|
|
return nil, fmt.Errorf("declaration must not end with %q [line: %d]", semicolon, ln.no) |
102 |
|
|
} |
103 |
|
|
|
104 |
|
|
return &declaration{ |
105 |
|
|
elementBase: newElementBase(ln, parent), |
106 |
|
|
property: property, |
107 |
|
|
value: value, |
108 |
|
|
}, nil |
109 |
|
|
} |