Skip to content

Commit 0042975

Browse files
Sujay JayakarConvex, Inc.
Sujay Jayakar
authored and
Convex, Inc.
committed
Add first component paths (#25416)
The general idea is that I'll change our function execution layers to accept a `ComponentFunctionPath` and then only support `ComponentId::Root` for now. GitOrigin-RevId: 2a53e96a98bfd3b62082737b448e43e3981c0928
1 parent 2a88609 commit 0042975

File tree

8 files changed

+158
-23
lines changed

8 files changed

+158
-23
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use std::{
2+
ops::Deref,
3+
path::{
4+
Component as PathComponent,
5+
PathBuf,
6+
},
7+
str::FromStr,
8+
};
9+
10+
use anyhow::Context;
11+
use sync_types::path::check_valid_path_component;
12+
13+
// Path relative to a project's `convex/` directory for each component
14+
// definition's folder. This path is project-level and originates from
15+
// a developer's source code.
16+
pub struct ComponentDefinitionPath {
17+
path: PathBuf,
18+
}
19+
20+
impl FromStr for ComponentDefinitionPath {
21+
type Err = anyhow::Error;
22+
23+
fn from_str(s: &str) -> Result<Self, Self::Err> {
24+
let path = PathBuf::from(s);
25+
for component in path.components() {
26+
match component {
27+
PathComponent::Normal(c) => {
28+
let s = c
29+
.to_str()
30+
.context("Path {s} has an invalid Unicode character")?;
31+
check_valid_path_component(s)?;
32+
},
33+
// Component paths are allowed to have `..` (since they're relative from the root
34+
// component's source directory).
35+
PathComponent::ParentDir => (),
36+
PathComponent::RootDir => {
37+
anyhow::bail!("Component paths must be relative ({s} is absolute).")
38+
},
39+
c => anyhow::bail!("Invalid path component {c:?} in {s}."),
40+
}
41+
}
42+
path.as_os_str()
43+
.to_str()
44+
.context("Path {s} has an invalid Unicode character")?;
45+
Ok(ComponentDefinitionPath { path })
46+
}
47+
}
48+
49+
impl Deref for ComponentDefinitionPath {
50+
type Target = str;
51+
52+
fn deref(&self) -> &Self::Target {
53+
self.path
54+
.as_os_str()
55+
.to_str()
56+
.expect("Invalid Unicode in ComponentDefinitionPath")
57+
}
58+
}
59+
60+
impl From<ComponentDefinitionPath> for String {
61+
fn from(value: ComponentDefinitionPath) -> Self {
62+
value
63+
.path
64+
.into_os_string()
65+
.into_string()
66+
.expect("Invalid Unicode in ComponentDefinitionPath?")
67+
}
68+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// All components under a component have a unique `ComponentName`. For example,
2+
// the root app component may have a waitlist component identified by
3+
// "chatWaitlist".
4+
#[allow(unused)]
5+
pub struct ComponentName {
6+
name: String,
7+
}
8+
9+
// Path within the component tree for a particular component. Note that this
10+
// path can potentially change when the component tree changes during a push, so
11+
// developers should resolve this path to a `ComponentId` within a transaction
12+
// as soon as possible.
13+
#[allow(unused)]
14+
pub struct ComponentPath {
15+
path: Vec<ComponentName>,
16+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use sync_types::{
2+
CanonicalizedUdfPath,
3+
UdfPath,
4+
};
5+
6+
use super::{
7+
component_definition_path::ComponentDefinitionPath,
8+
ComponentId,
9+
};
10+
11+
pub struct ComponentDefinitionFunctionPath {
12+
pub component: ComponentDefinitionPath,
13+
pub udf_path: UdfPath,
14+
}
15+
16+
pub struct ComponentFunctionPath {
17+
pub component: ComponentId,
18+
pub udf_path: UdfPath,
19+
}
20+
21+
pub struct CanonicalizedComponentFunctionPath {
22+
pub component: ComponentId,
23+
pub udf_path: CanonicalizedUdfPath,
24+
}

crates/common/src/components/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use value::InternalDocumentId;
2+
3+
mod component_definition_path;
4+
mod component_path;
5+
mod function_paths;
6+
7+
pub use self::{
8+
component_definition_path::ComponentDefinitionPath,
9+
component_path::{
10+
ComponentName,
11+
ComponentPath,
12+
},
13+
function_paths::{
14+
CanonicalizedComponentFunctionPath,
15+
ComponentDefinitionFunctionPath,
16+
ComponentFunctionPath,
17+
},
18+
};
19+
20+
// Globally unique system-assigned ID for a component.
21+
pub enum ComponentId {
22+
Root,
23+
Child(InternalDocumentId),
24+
}

crates/common/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub mod bounds;
3232
pub mod client_pool;
3333
pub mod codel_queue;
3434
pub mod comparators;
35+
pub mod components;
3536
pub mod deleted_bitset;
3637
pub mod document;
3738
pub mod errors;

crates/convex/sync_types/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub mod headers;
44
pub mod identifier;
55
pub mod json;
66
pub mod module_path;
7+
pub mod path;
78
#[cfg(any(test, feature = "testing"))]
89
pub mod testing;
910
pub mod timestamp;

crates/convex/sync_types/src/module_path.rs

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::{
77
str::FromStr,
88
};
99

10-
use crate::identifier::MAX_IDENTIFIER_LEN;
10+
use crate::path::check_valid_path_component;
1111

1212
pub const SYSTEM_UDF_DIR: &str = "_system";
1313
pub const DEPS_DIR: &str = "_deps";
@@ -100,28 +100,6 @@ fn canonicalize_path_buf(mut path: PathBuf) -> PathBuf {
100100
path
101101
}
102102

103-
fn check_valid_path_component(s: &str) -> anyhow::Result<()> {
104-
if s.len() > MAX_IDENTIFIER_LEN {
105-
anyhow::bail!(
106-
"Path component is too long ({} > maximum {}).",
107-
s.len(),
108-
MAX_IDENTIFIER_LEN
109-
);
110-
}
111-
if !s
112-
.chars()
113-
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.')
114-
{
115-
anyhow::bail!(
116-
"Path component {s} can only contain alphanumeric characters, underscores, or periods."
117-
);
118-
}
119-
if !s.chars().any(|c| c.is_ascii_alphanumeric()) {
120-
anyhow::bail!("Path component {s} must have at least one alphanumeric character.");
121-
}
122-
Ok(())
123-
}
124-
125103
/// Parse a module path from a `str`.
126104
impl FromStr for ModulePath {
127105
type Err = anyhow::Error;

crates/convex/sync_types/src/path.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use crate::identifier::MAX_IDENTIFIER_LEN;
2+
3+
pub fn check_valid_path_component(s: &str) -> anyhow::Result<()> {
4+
if s.len() > MAX_IDENTIFIER_LEN {
5+
anyhow::bail!(
6+
"Path component is too long ({} > maximum {}).",
7+
s.len(),
8+
MAX_IDENTIFIER_LEN
9+
);
10+
}
11+
if !s
12+
.chars()
13+
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.')
14+
{
15+
anyhow::bail!(
16+
"Path component {s} can only contain alphanumeric characters, underscores, or periods."
17+
);
18+
}
19+
if !s.chars().any(|c| c.is_ascii_alphanumeric()) {
20+
anyhow::bail!("Path component {s} must have at least one alphanumeric character.");
21+
}
22+
Ok(())
23+
}

0 commit comments

Comments
 (0)