ntex/web/
rmap.rs

1use std::{cell::RefCell, rc::Rc};
2
3#[cfg(feature = "url")]
4use url_pkg::Url;
5
6use crate::router::ResourceDef;
7use crate::util::HashMap;
8#[cfg(feature = "url")]
9use crate::web::httprequest::HttpRequest;
10
11#[derive(Clone, Debug)]
12pub struct ResourceMap {
13    #[allow(dead_code)]
14    root: ResourceDef,
15    parent: RefCell<Option<Rc<ResourceMap>>>,
16    named: HashMap<String, ResourceDef>,
17    patterns: Vec<(ResourceDef, Option<Rc<ResourceMap>>)>,
18}
19
20impl ResourceMap {
21    pub fn new(root: ResourceDef) -> Self {
22        ResourceMap {
23            root,
24            parent: RefCell::new(None),
25            named: HashMap::default(),
26            patterns: Vec::new(),
27        }
28    }
29
30    pub fn add(&mut self, pattern: &mut ResourceDef, nested: Option<Rc<ResourceMap>>) {
31        pattern.set_id(self.patterns.len() as u16);
32        self.patterns.push((pattern.clone(), nested));
33        if !pattern.name().is_empty() {
34            self.named
35                .insert(pattern.name().to_string(), pattern.clone());
36        }
37    }
38
39    pub(crate) fn finish(&self, current: Rc<ResourceMap>) {
40        for (_, nested) in &self.patterns {
41            if let Some(ref nested) = nested {
42                *nested.parent.borrow_mut() = Some(current.clone());
43                nested.finish(nested.clone());
44            }
45        }
46    }
47}
48
49#[cfg(feature = "url")]
50impl ResourceMap {
51    /// Generate url for named resource
52    ///
53    /// Check [`HttpRequest::url_for()`](../struct.HttpRequest.html#method.
54    /// url_for) for detailed information.
55    pub fn url_for<U, I>(
56        &self,
57        req: &HttpRequest,
58        name: &str,
59        elements: U,
60    ) -> Result<Url, super::error::UrlGenerationError>
61    where
62        U: IntoIterator<Item = I>,
63        I: AsRef<str>,
64    {
65        let mut path = String::new();
66        let mut elements = elements.into_iter();
67
68        if self.patterns_for(name, &mut path, &mut elements)?.is_some() {
69            if path.starts_with('/') {
70                let conn = req.connection_info();
71                Ok(Url::parse(&format!(
72                    "{}://{}{}",
73                    conn.scheme(),
74                    conn.host(),
75                    path
76                ))?)
77            } else {
78                Ok(Url::parse(&path)?)
79            }
80        } else {
81            Err(super::error::UrlGenerationError::ResourceNotFound)
82        }
83    }
84
85    // pub fn has_resource(&self, path: &str) -> bool {
86    // let _path = if path.is_empty() { "/" } else { path };
87
88    // for (pattern, rmap) in &self.patterns {
89    //     if let Some(ref rmap) = rmap {
90    //         if let Some(plen) = pattern.is_prefix_match(path) {
91    //             return rmap.has_resource(&path[plen..]);
92    //         }
93    //     } else if pattern.is_match(path) {
94    //         return true;
95    //     }
96    // }
97    // false
98    // }
99
100    fn patterns_for<U, I>(
101        &self,
102        name: &str,
103        path: &mut String,
104        elements: &mut U,
105    ) -> Result<Option<()>, super::error::UrlGenerationError>
106    where
107        U: Iterator<Item = I>,
108        I: AsRef<str>,
109    {
110        if self.pattern_for(name, path, elements)?.is_some() {
111            Ok(Some(()))
112        } else {
113            self.parent_pattern_for(name, path, elements)
114        }
115    }
116
117    fn pattern_for<U, I>(
118        &self,
119        name: &str,
120        path: &mut String,
121        elements: &mut U,
122    ) -> Result<Option<()>, super::error::UrlGenerationError>
123    where
124        U: Iterator<Item = I>,
125        I: AsRef<str>,
126    {
127        if let Some(pattern) = self.named.get(name) {
128            if pattern.pattern().starts_with('/') {
129                self.fill_root(path, elements)?;
130            }
131            if pattern.resource_path(path, elements) {
132                Ok(Some(()))
133            } else {
134                Err(super::error::UrlGenerationError::NotEnoughElements)
135            }
136        } else {
137            for (_, rmap) in &self.patterns {
138                if let Some(ref rmap) = rmap {
139                    if rmap.pattern_for(name, path, elements)?.is_some() {
140                        return Ok(Some(()));
141                    }
142                }
143            }
144            Ok(None)
145        }
146    }
147
148    fn fill_root<U, I>(
149        &self,
150        path: &mut String,
151        elements: &mut U,
152    ) -> Result<(), super::error::UrlGenerationError>
153    where
154        U: Iterator<Item = I>,
155        I: AsRef<str>,
156    {
157        if let Some(ref parent) = *self.parent.borrow() {
158            parent.fill_root(path, elements)?;
159        }
160        if self.root.resource_path(path, elements) {
161            Ok(())
162        } else {
163            Err(super::error::UrlGenerationError::NotEnoughElements)
164        }
165    }
166
167    fn parent_pattern_for<U, I>(
168        &self,
169        name: &str,
170        path: &mut String,
171        elements: &mut U,
172    ) -> Result<Option<()>, super::error::UrlGenerationError>
173    where
174        U: Iterator<Item = I>,
175        I: AsRef<str>,
176    {
177        if let Some(ref parent) = *self.parent.borrow() {
178            if let Some(pattern) = parent.named.get(name) {
179                self.fill_root(path, elements)?;
180                if pattern.resource_path(path, elements) {
181                    Ok(Some(()))
182                } else {
183                    Err(super::error::UrlGenerationError::NotEnoughElements)
184                }
185            } else {
186                parent.parent_pattern_for(name, path, elements)
187            }
188        } else {
189            Ok(None)
190        }
191    }
192}