aboutsummaryrefslogtreecommitdiff
path: root/macros/src/check.rs
blob: 3cd112acfd1758d2847365cd91434dd005151d41 (plain) (blame)
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
use std::collections::HashMap;

use syn::{Ident, Path};
use syntax::check::{self, Idle, Init};
use syntax::{self, Resources, Statics};

use syntax::error::*;

pub struct App {
    pub device: Path,
    pub idle: Idle,
    pub init: Init,
    pub resources: Statics,
    pub tasks: Tasks,
}

pub type Tasks = HashMap<Ident, Task>;

#[allow(non_camel_case_types)]
pub enum Exception {
    PENDSV,
    SVCALL,
    SYS_TICK,
}

impl Exception {
    pub fn from(s: &str) -> Option<Self> {
        Some(match s {
            "PENDSV" => Exception::PENDSV,
            "SVCALL" => Exception::SVCALL,
            "SYS_TICK" => Exception::SYS_TICK,
            _ => return None,
        })
    }

    pub fn nr(&self) -> usize {
        match *self {
            Exception::PENDSV => 14,
            Exception::SVCALL => 11,
            Exception::SYS_TICK => 15,
        }
    }
}

pub enum Kind {
    Exception(Exception),
    Interrupt { enabled: bool },
}

pub struct Task {
    pub kind: Kind,
    pub path: Path,
    pub priority: u8,
    pub resources: Resources,
}

pub fn app(app: check::App) -> Result<App> {
    let app = App {
        device: app.device,
        idle: app.idle,
        init: app.init,
        resources: app.resources,
        tasks: app.tasks
            .into_iter()
            .map(|(k, v)| {
                let v = ::check::task(k.as_ref(), v)
                    .chain_err(|| format!("checking task `{}`", k))?;

                Ok((k, v))
            })
            .collect::<Result<_>>()?,
    };

    ::check::resources(&app)
        .chain_err(|| "checking `resources`")?;

    Ok(app)
}

fn resources(app: &App) -> Result<()> {
    for resource in app.resources.keys() {
        if app.idle.resources.contains(resource) {
            continue;
        }

        if app.tasks
            .values()
            .any(|task| task.resources.contains(resource))
        {
            continue;
        }

        bail!("resource `{}` is unused", resource);
    }

    Ok(())
}

fn task(name: &str, task: syntax::check::Task) -> Result<Task> {
    let kind = match Exception::from(name) {
        Some(e) => {
            ensure!(
                task.enabled.is_none(),
                "`enabled` field is not valid for exceptions"
            );

            Kind::Exception(e)
        }
        None => {
            if task.enabled == Some(true) {
                bail!(
                    "`enabled: true` is the default value; this line can be \
                     omitted"
                );
            }

            Kind::Interrupt {
                enabled: task.enabled.unwrap_or(true),
            }
        }
    };

    Ok(Task {
        kind,
        path: task.path.ok_or("`path` field is missing")?,
        priority: task.priority.unwrap_or(1),
        resources: task.resources,
    })
}