1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
use super::{
    CallArgs, Callable, Name, SassString, Selectors, Value,
    VariableDeclaration,
};
use crate::input::SourcePos;
use std::collections::BTreeSet;

/// Every sass file is a sequence of sass items.
/// Scoping items contains further sequences of items.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd)]
pub enum Item {
    /// An `@import` directive.
    Import(Vec<SassString>, Value, SourcePos),
    /// A variable declaration.
    VariableDeclaration(VariableDeclaration),

    /// An `@at-root` directive.
    AtRoot(Selectors, Vec<Item>),
    /// An `@media` directive.
    AtMedia {
        /// Any arguments
        args: Value,
        /// The directive may have a body.
        body: Option<Vec<Item>>,
        /// The source location of this at rule.
        pos: SourcePos,
    },
    /// A generic `@` directive.
    AtRule {
        /// The name of this directive
        name: SassString,
        /// Any arguments
        args: Value,
        /// The directive may have a body.
        body: Option<Vec<Item>>,
        /// The source location of this at rule.
        pos: SourcePos,
    },
    /// An `@debug` directive.
    Debug(Value),
    /// An `@warn` directive.
    Warn(Value),
    /// An `@error` directive.
    Error(Value, SourcePos),

    /// A `@mixin` directive, declaring a mixin.
    MixinDeclaration(String, Callable),
    /// An `@include` directive, calling a mixin.
    MixinCall(String, CallArgs, Option<Callable>, SourcePos),
    /// An `@content` directive (in a mixin declaration).
    Content(CallArgs, SourcePos),

    /// An `@function` declaration.
    FunctionDeclaration(String, Callable),
    /// An `@return` statement in a function declaration.
    Return(Value, SourcePos),

    /// An `@if` conditional directive.
    IfStatement(Value, Vec<Item>, Vec<Item>),
    /// An `@each` loop directive.
    ///
    /// The value may be or evaluate to a list.
    Each(Vec<Name>, Value, Vec<Item>),
    /// An `@for` loop directive.
    For {
        /// The name of the iteration variable.
        name: Name,
        /// The start value for the iteration.
        from: Box<Value>,
        /// The end value for the iteration.
        to: Box<Value>,
        /// True if the end should be included in the range.
        inclusive: bool,
        /// The body of the loop.
        body: Vec<Item>,
    },
    /// An `@while` loop directive.
    While(Value, Vec<Item>),

    /// An `@use` directive.
    Use(SassString, UseAs, Vec<(Name, Value, bool)>, SourcePos),
    /// An `@forward` directive.
    Forward(
        SassString,
        UseAs,
        Expose,
        Vec<(Name, Value, bool)>,
        SourcePos,
    ),

    /// Extend rule (not really supported yet).
    Extend(Selectors),

    /// A sass rule; selectors followed by a block of items.
    Rule(Selectors, Vec<Item>),
    /// A sass namespace rule; a name followed by a block of properties.
    NamespaceRule(SassString, Value, Vec<Item>),
    /// A sass property; a name and a value.
    /// The position is the full value.
    Property(SassString, Value, SourcePos),
    /// A custom property.
    CustomProperty(SassString, SassString),
    /// A comment (that might be preserved for the output).
    Comment(SassString),
    /// Nothing
    None,
}

impl From<VariableDeclaration> for Item {
    fn from(value: VariableDeclaration) -> Self {
        Self::VariableDeclaration(value)
    }
}

/// How an `@forward`-ed module should be exposed.
///
/// As directed by the `show` or `hide` keywords or their absense.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd)]
pub enum Expose {
    /// No `show` or `hide` specified; expose everything.
    All,
    /// Only show the listed items.
    ///
    /// The first list is functions and mixins, the second is variables.
    Show(BTreeSet<Name>, BTreeSet<Name>),
    /// Hide the listed items, show everything else.
    ///
    /// The first list is functions and mixins, the second is variables.
    Hide(BTreeSet<Name>, BTreeSet<Name>),
}

impl Expose {
    /// Check if `name` should be exposed as a function/mixin.
    pub fn allow_fun(&self, name: &Name) -> bool {
        match self {
            Self::All => true,
            Self::Show(show, _) => show.contains(name),
            Self::Hide(hide, _) => !hide.contains(name),
        }
    }
    /// Check if `name` should be exposed as a variable.
    pub fn allow_var(&self, name: &Name) -> bool {
        match self {
            Self::All => true,
            Self::Show(_, show) => show.contains(name),
            Self::Hide(_, hide) => !hide.contains(name),
        }
    }
}

/// The `as` part of an `@use` or `@forward` directive.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd)]
pub enum UseAs {
    /// A plain `@use foo;`.
    KeepName,
    /// Include the module contents directly in the scope, `@use foo as *;`.
    Star,
    /// An explicit name, `@use foo as bar`.
    Name(String),
    /// A prefix, `@forward foo as foo-*`.
    Prefix(String),
}